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.List; 50 import java.util.Locale; 51 import java.util.Objects; 52 import java.util.Set; 53 54 /** 55 * Concrete implementation of the {@link java.text.spi.NumberFormatProvider 56 * NumberFormatProvider} class for the JRE LocaleProviderAdapter. 57 * 58 * @author Naoto Sato 59 * @author Masayoshi Okutsu 60 */ 61 public class NumberFormatProviderImpl extends NumberFormatProvider implements AvailableLanguageTags { 62 63 // Constants used by factory methods to specify a style of format. 64 private static final int NUMBERSTYLE = 0; 65 private static final int CURRENCYSTYLE = 1; 66 private static final int PERCENTSTYLE = 2; 67 private static final int SCIENTIFICSTYLE = 3; 68 private static final int INTEGERSTYLE = 4; 69 70 private final LocaleProviderAdapter.Type type; 71 private final Set<String> langtags; 72 73 public NumberFormatProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) { 74 this.type = type; 75 this.langtags = langtags; 76 } 77 78 /** 79 * Returns an array of all locales for which this locale service provider 80 * can provide localized objects or names. 81 * 82 * @return An array of all locales for which this locale service provider 83 * can provide localized objects or names. 84 */ 85 @Override 86 public Locale[] getAvailableLocales() { 87 return LocaleProviderAdapter.forType(type).getAvailableLocales(); 88 } 89 90 @Override 91 public boolean isSupportedLocale(Locale locale) { 92 return LocaleProviderAdapter.forType(type).isSupportedProviderLocale(locale, langtags); 93 } 94 95 /** 96 * Returns a new <code>NumberFormat</code> instance which formats 97 * monetary values for the specified locale. 98 * 99 * @param locale the desired locale. 100 * @exception NullPointerException if <code>locale</code> is null 101 * @exception IllegalArgumentException if <code>locale</code> isn't 102 * one of the locales returned from 103 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 104 * getAvailableLocales()}. 105 * @return a currency formatter 106 * @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale) 107 */ 108 @Override 109 public NumberFormat getCurrencyInstance(Locale locale) { 110 return getInstance(locale, CURRENCYSTYLE); 111 } 112 113 /** 114 * Returns a new <code>NumberFormat</code> instance which formats 115 * integer values for the specified locale. 116 * The returned number format is configured to 117 * round floating point numbers to the nearest integer using 118 * half-even rounding (see {@link java.math.RoundingMode#HALF_EVEN HALF_EVEN}) 119 * for formatting, and to parse only the integer part of 120 * an input string (see {@link 121 * java.text.NumberFormat#isParseIntegerOnly isParseIntegerOnly}). 122 * 123 * @param locale the desired locale 124 * @exception NullPointerException if <code>locale</code> is null 125 * @exception IllegalArgumentException if <code>locale</code> isn't 126 * one of the locales returned from 127 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 128 * getAvailableLocales()}. 129 * @return a number format for integer values 130 * @see java.text.NumberFormat#getIntegerInstance(java.util.Locale) 131 */ 132 @Override 133 public NumberFormat getIntegerInstance(Locale locale) { 134 return getInstance(locale, INTEGERSTYLE); 135 } 136 137 /** 138 * Returns a new general-purpose <code>NumberFormat</code> instance for 139 * the specified locale. 140 * 141 * @param locale the desired locale 142 * @exception NullPointerException if <code>locale</code> is null 143 * @exception IllegalArgumentException if <code>locale</code> isn't 144 * one of the locales returned from 145 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 146 * getAvailableLocales()}. 147 * @return a general-purpose number formatter 148 * @see java.text.NumberFormat#getNumberInstance(java.util.Locale) 149 */ 150 @Override 151 public NumberFormat getNumberInstance(Locale locale) { 152 return getInstance(locale, NUMBERSTYLE); 153 } 154 155 /** 156 * Returns a new <code>NumberFormat</code> instance which formats 157 * percentage values for the specified locale. 158 * 159 * @param locale the desired locale 160 * @exception NullPointerException if <code>locale</code> is null 161 * @exception IllegalArgumentException if <code>locale</code> isn't 162 * one of the locales returned from 163 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 164 * getAvailableLocales()}. 165 * @return a percent formatter 166 * @see java.text.NumberFormat#getPercentInstance(java.util.Locale) 167 */ 168 @Override 169 public NumberFormat getPercentInstance(Locale locale) { 170 return getInstance(locale, PERCENTSTYLE); 171 } 172 173 private NumberFormat getInstance(Locale locale, 174 int choice) { 175 if (locale == null) { 176 throw new NullPointerException(); 177 } 178 179 // Check for region override 180 Locale override = locale.getUnicodeLocaleType("nu") == null ? 181 CalendarDataUtility.findRegionOverride(locale) : 182 locale; 183 184 LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); 185 String[] numberPatterns = adapter.getLocaleResources(override).getNumberPatterns(); 186 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override); 187 int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice; 188 DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols); 189 190 if (choice == INTEGERSTYLE) { 191 format.setMaximumFractionDigits(0); 192 format.setDecimalSeparatorAlwaysShown(false); 193 format.setParseIntegerOnly(true); 194 } else if (choice == CURRENCYSTYLE) { 195 adjustForCurrencyDefaultFractionDigits(format, symbols); 196 } 197 198 return format; 199 } 200 201 /** 202 * Adjusts the minimum and maximum fraction digits to values that 203 * are reasonable for the currency's default fraction digits. 204 */ 205 private static void adjustForCurrencyDefaultFractionDigits( 206 DecimalFormat format, DecimalFormatSymbols symbols) { 207 Currency currency = symbols.getCurrency(); 208 if (currency == null) { 209 try { 210 currency = Currency.getInstance(symbols.getInternationalCurrencySymbol()); 211 } catch (IllegalArgumentException e) { 212 } 213 } 214 if (currency != null) { 215 int digits = currency.getDefaultFractionDigits(); 216 if (digits != -1) { 217 int oldMinDigits = format.getMinimumFractionDigits(); 218 // Common patterns are "#.##", "#.00", "#". 219 // Try to adjust all of them in a reasonable way. 220 if (oldMinDigits == format.getMaximumFractionDigits()) { 221 format.setMinimumFractionDigits(digits); 222 format.setMaximumFractionDigits(digits); 223 } else { 224 format.setMinimumFractionDigits(Math.min(digits, oldMinDigits)); 225 format.setMaximumFractionDigits(digits); 226 } 227 } 228 } 229 } 230 231 /** 232 * Returns a new {@code NumberFormat} instance which formats 233 * a number in its compact form for the specified 234 * {@code locale} and {@code formatStyle}. 235 * 236 * @param locale the desired locale 237 * @param formatStyle the style for formatting a number 238 * @throws NullPointerException if {@code locale} or {@code formatStyle} 239 * is {@code null} 240 * @throws IllegalArgumentException if {@code locale} isn't 241 * one of the locales returned from 242 * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() 243 * getAvailableLocales()}. 244 * @return a compact number formatter 245 * 246 * @see java.text.NumberFormat#getCompactNumberInstance(Locale, 247 * NumberFormat.Style) 248 * @since 12 249 */ 250 @Override 251 public NumberFormat getCompactNumberInstance(Locale locale, 252 NumberFormat.Style formatStyle) { 253 254 Objects.requireNonNull(locale); 255 Objects.requireNonNull(formatStyle); 256 257 // Check for region override 258 Locale override = locale.getUnicodeLocaleType("nu") == null 259 ? CalendarDataUtility.findRegionOverride(locale) 260 : locale; 261 262 LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); 263 LocaleResources resource = adapter.getLocaleResources(override); 264 265 String[] numberPatterns = resource.getNumberPatterns(); 266 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override); 267 List<String> cnPatterns = resource.getCNPatterns(formatStyle); 268 269 CompactNumberFormat format = new CompactNumberFormat(numberPatterns[0], 270 symbols, cnPatterns.toArray(new String[cnPatterns.size()])); 271 return format; 272 } 273 274 @Override 275 public Set<String> getAvailableLanguageTags() { 276 return langtags; 277 } 278 }