1 /* 2 * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * 28 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved 29 * (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved 30 * 31 * The original version of this source code and documentation 32 * is copyrighted and owned by Taligent, Inc., a wholly-owned 33 * subsidiary of IBM. These materials are provided under terms 34 * of a License Agreement between Taligent and Sun. This technology 35 * is protected by multiple US and International patents. 36 * 37 * This notice and attribution to Taligent may not be removed. 38 * Taligent is a registered trademark of Taligent, Inc. 39 */ 40 41 package sun.util.locale.provider; 42 43 import java.text.CompactNumberFormat; 44 import java.text.DecimalFormat; 45 import java.text.DecimalFormatSymbols; 46 import java.text.NumberFormat; 47 import java.text.spi.NumberFormatProvider; 48 import java.util.Currency; 49 import java.util.Locale; 50 import java.util.Objects; 51 import java.util.Set; 52 53 /** 54 * Concrete implementation of the {@link java.text.spi.NumberFormatProvider 55 * NumberFormatProvider} class for the JRE LocaleProviderAdapter. 56 * 57 * @author Naoto Sato 58 * @author Masayoshi Okutsu 59 */ 60 public class NumberFormatProviderImpl extends NumberFormatProvider implements AvailableLanguageTags { 61 62 // Constants used by factory methods to specify a style of format. 63 private static final int NUMBERSTYLE = 0; 64 private static final int CURRENCYSTYLE = 1; 65 private static final int PERCENTSTYLE = 2; 66 private static final int SCIENTIFICSTYLE = 3; 67 private static final int INTEGERSTYLE = 4; 68 69 private final LocaleProviderAdapter.Type type; 70 private final Set<String> langtags; 71 72 public NumberFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) { 73 this.type = type; 74 this.langtags = langtags; 75 } 76 77 /** 78 * Returns an array of all locales for which this locale service provider 79 * can provide localized objects or names. 80 * 81 * @return An array of all locales for which this locale service provider 82 * can provide localized objects or names. 83 */ 84 @Override 85 public Locale[] getAvailableLocales() { 86 return LocaleProviderAdapter.forType(type).getAvailableLocales(); 87 } 88 89 @Override 90 public boolean isSupportedLocale(Locale locale) { 91 return LocaleProviderAdapter.forType(type).isSupportedProviderLocale(locale, langtags); 92 } 93 94 /** 95 * Returns a new <code>NumberFormat</code> instance which formats 96 * monetary values for the specified locale. 97 * 98 * @param locale the desired locale. 99 * @exception NullPointerException if <code>locale</code> is null 100 * @exception IllegalArgumentException if <code>locale</code> isn't 101 * one of the locales returned from 102 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 103 * getAvailableLocales()}. 104 * @return a currency formatter 105 * @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale) 106 */ 107 @Override 108 public NumberFormat getCurrencyInstance(Locale locale) { 109 return getInstance(locale, CURRENCYSTYLE); 110 } 111 112 /** 113 * Returns a new <code>NumberFormat</code> instance which formats 114 * integer values for the specified locale. 115 * The returned number format is configured to 116 * round floating point numbers to the nearest integer using 117 * half-even rounding (see {@link java.math.RoundingMode#HALF_EVEN HALF_EVEN}) 118 * for formatting, and to parse only the integer part of 119 * an input string (see {@link 120 * java.text.NumberFormat#isParseIntegerOnly isParseIntegerOnly}). 121 * 122 * @param locale the desired locale 123 * @exception NullPointerException if <code>locale</code> is null 124 * @exception IllegalArgumentException if <code>locale</code> isn't 125 * one of the locales returned from 126 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 127 * getAvailableLocales()}. 128 * @return a number format for integer values 129 * @see java.text.NumberFormat#getIntegerInstance(java.util.Locale) 130 */ 131 @Override 132 public NumberFormat getIntegerInstance(Locale locale) { 133 return getInstance(locale, INTEGERSTYLE); 134 } 135 136 /** 137 * Returns a new general-purpose <code>NumberFormat</code> instance for 138 * the specified locale. 139 * 140 * @param locale the desired locale 141 * @exception NullPointerException if <code>locale</code> is null 142 * @exception IllegalArgumentException if <code>locale</code> isn't 143 * one of the locales returned from 144 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 145 * getAvailableLocales()}. 146 * @return a general-purpose number formatter 147 * @see java.text.NumberFormat#getNumberInstance(java.util.Locale) 148 */ 149 @Override 150 public NumberFormat getNumberInstance(Locale locale) { 151 return getInstance(locale, NUMBERSTYLE); 152 } 153 154 /** 155 * Returns a new <code>NumberFormat</code> instance which formats 156 * percentage values for the specified locale. 157 * 158 * @param locale the desired locale 159 * @exception NullPointerException if <code>locale</code> is null 160 * @exception IllegalArgumentException if <code>locale</code> isn't 161 * one of the locales returned from 162 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 163 * getAvailableLocales()}. 164 * @return a percent formatter 165 * @see java.text.NumberFormat#getPercentInstance(java.util.Locale) 166 */ 167 @Override 168 public NumberFormat getPercentInstance(Locale locale) { 169 return getInstance(locale, PERCENTSTYLE); 170 } 171 172 private NumberFormat getInstance(Locale locale, 173 int choice) { 174 if (locale == null) { 175 throw new NullPointerException(); 176 } 177 178 // Check for region override 179 Locale override = locale.getUnicodeLocaleType("nu") == null ? 180 CalendarDataUtility.findRegionOverride(locale) : 181 locale; 182 183 LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); 184 String[] numberPatterns = adapter.getLocaleResources(override).getNumberPatterns(); 185 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override); 186 int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice; 187 DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols); 188 189 if (choice == INTEGERSTYLE) { 190 format.setMaximumFractionDigits(0); 191 format.setDecimalSeparatorAlwaysShown(false); 192 format.setParseIntegerOnly(true); 193 } else if (choice == CURRENCYSTYLE) { 194 adjustForCurrencyDefaultFractionDigits(format, symbols); 195 } 196 197 return format; 198 } 199 200 /** 201 * Adjusts the minimum and maximum fraction digits to values that 202 * are reasonable for the currency's default fraction digits. 203 */ 204 private static void adjustForCurrencyDefaultFractionDigits( 205 DecimalFormat format, DecimalFormatSymbols symbols) { 206 Currency currency = symbols.getCurrency(); 207 if (currency == null) { 208 try { 209 currency = Currency.getInstance(symbols.getInternationalCurrencySymbol()); 210 } catch (IllegalArgumentException e) { 211 } 212 } 213 if (currency != null) { 214 int digits = currency.getDefaultFractionDigits(); 215 if (digits != -1) { 216 int oldMinDigits = format.getMinimumFractionDigits(); 217 // Common patterns are "#.##", "#.00", "#". 218 // Try to adjust all of them in a reasonable way. 219 if (oldMinDigits == format.getMaximumFractionDigits()) { 220 format.setMinimumFractionDigits(digits); 221 format.setMaximumFractionDigits(digits); 222 } else { 223 format.setMinimumFractionDigits(Math.min(digits, oldMinDigits)); 224 format.setMaximumFractionDigits(digits); 225 } 226 } 227 } 228 } 229 230 /** 231 * Returns a new {@code NumberFormat} instance which formats 232 * a number in its compact form for the specified 233 * {@code locale} and {@code formatStyle}. 234 * 235 * @param locale the desired locale 236 * @param formatStyle the style for formatting a number 237 * @throws NullPointerException if {@code locale} or {@code formatStyle} 238 * is {@code null} 239 * @throws IllegalArgumentException if {@code locale} isn't 240 * one of the locales returned from 241 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 242 * getAvailableLocales()}. 243 * @return a compact number formatter 244 * 245 * @see java.text.NumberFormat#getCompactNumberInstance(Locale, 246 * NumberFormat.Style) 247 * @since 12 248 */ 249 @Override 250 public NumberFormat getCompactNumberInstance(Locale locale, 251 NumberFormat.Style formatStyle) { 252 253 Objects.requireNonNull(locale); 254 Objects.requireNonNull(formatStyle); 255 256 // Check for region override 257 Locale override = locale.getUnicodeLocaleType("nu") == null 258 ? CalendarDataUtility.findRegionOverride(locale) 259 : locale; 260 261 LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); 262 LocaleResources resource = adapter.getLocaleResources(override); 263 264 String[] numberPatterns = resource.getNumberPatterns(); 265 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override); 266 String[] cnPatterns = resource.getCNPatterns(formatStyle); 267 268 CompactNumberFormat format = new CompactNumberFormat(numberPatterns[0], 269 symbols, cnPatterns); 270 return format; 271 } 272 273 @Override 274 public Set<String> getAvailableLanguageTags() { 275 return langtags; 276 } 277 }