1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.text; 19 20import java.io.IOException; 21import java.io.ObjectInputStream; 22import java.io.ObjectOutputStream; 23import java.io.ObjectStreamField; 24import java.io.Serializable; 25import java.util.Arrays; 26import java.util.Currency; 27import java.util.Locale; 28 29import com.ibm.icu4jni.util.LocaleData; 30 31/** 32 * Encapsulates the set of symbols (such as the decimal separator, the grouping 33 * separator, and so on) needed by {@code DecimalFormat} to format numbers. 34 * {@code DecimalFormat} internally creates an instance of 35 * {@code DecimalFormatSymbols} from its locale data. If you need to change any 36 * of these symbols, you can get the {@code DecimalFormatSymbols} object from 37 * your {@code DecimalFormat} and modify it. 38 * 39 * @see java.util.Locale 40 * @see DecimalFormat 41 */ 42public final class DecimalFormatSymbols implements Cloneable, Serializable { 43 44 private static final long serialVersionUID = 5772796243397350300L; 45 46 private final int ZeroDigit = 0, Digit = 1, DecimalSeparator = 2, 47 GroupingSeparator = 3, PatternSeparator = 4, Percent = 5, 48 PerMill = 6, Exponent = 7, MonetaryDecimalSeparator = 8, 49 MinusSign = 9; 50 51 private transient char[] patternChars; 52 53 private transient Currency currency; 54 55 private transient Locale locale; 56 57 private String infinity, NaN, currencySymbol, intlCurrencySymbol; 58 59 /** 60 * Constructs a new {@code DecimalFormatSymbols} containing the symbols for 61 * the default locale. Best practice is to create a {@code DecimalFormat} 62 * and then to get the {@code DecimalFormatSymbols} from that object by 63 * calling {@link DecimalFormat#getDecimalFormatSymbols()}. 64 */ 65 public DecimalFormatSymbols() { 66 this(Locale.getDefault()); 67 } 68 69 /** 70 * Constructs a new DecimalFormatSymbols containing the symbols for the 71 * specified Locale. Best practice is to create a {@code DecimalFormat} 72 * and then to get the {@code DecimalFormatSymbols} from that object by 73 * calling {@link DecimalFormat#getDecimalFormatSymbols()}. 74 * 75 * @param locale 76 * the locale. 77 */ 78 public DecimalFormatSymbols(Locale locale) { 79 // BEGIN android-changed 80 LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); 81 this.patternChars = localeData.decimalPatternChars.toCharArray(); 82 this.infinity = localeData.infinity; 83 this.NaN = localeData.NaN; 84 this.locale = locale; 85 try { 86 currency = Currency.getInstance(locale); 87 currencySymbol = currency.getSymbol(locale); 88 intlCurrencySymbol = currency.getCurrencyCode(); 89 } catch (IllegalArgumentException e) { 90 currency = Currency.getInstance("XXX"); //$NON-NLS-1$ 91 currencySymbol = localeData.currencySymbol; 92 intlCurrencySymbol = localeData.internationalCurrencySymbol; 93 } 94 // END android-changed 95 } 96 97 98 /** 99 * Returns a new {@code DecimalFormatSymbols} with the same symbols as this 100 * {@code DecimalFormatSymbols}. 101 * 102 * @return a shallow copy of this {@code DecimalFormatSymbols}. 103 * 104 * @see java.lang.Cloneable 105 */ 106 @Override 107 public Object clone() { 108 try { 109 DecimalFormatSymbols symbols = (DecimalFormatSymbols) super.clone(); 110 symbols.patternChars = patternChars.clone(); 111 return symbols; 112 } catch (CloneNotSupportedException e) { 113 throw new AssertionError(e); // android-changed 114 } 115 } 116 117 /** 118 * Compares the specified object to this {@code DecimalFormatSymbols} and 119 * indicates if they are equal. In order to be equal, {@code object} must be 120 * an instance of {@code DecimalFormatSymbols} and contain the same symbols. 121 * 122 * @param object 123 * the object to compare with this object. 124 * @return {@code true} if the specified object is equal to this 125 * {@code DecimalFormatSymbols}; {@code false} otherwise. 126 * @see #hashCode 127 */ 128 @Override 129 public boolean equals(Object object) { 130 if (this == object) { 131 return true; 132 } 133 if (!(object instanceof DecimalFormatSymbols)) { 134 return false; 135 } 136 DecimalFormatSymbols obj = (DecimalFormatSymbols) object; 137 return Arrays.equals(patternChars, obj.patternChars) 138 && infinity.equals(obj.infinity) && NaN.equals(obj.NaN) 139 && currencySymbol.equals(obj.currencySymbol) 140 && intlCurrencySymbol.equals(obj.intlCurrencySymbol); 141 } 142 143 /** 144 * Returns the currency. 145 * <p> 146 * {@code null} is returned if {@code setInternationalCurrencySymbol()} has 147 * been previously called with a value that is not a valid ISO 4217 currency 148 * code. 149 * <p> 150 * 151 * @return the currency that was set in the constructor or by calling 152 * {@code setCurrency()} or {@code setInternationalCurrencySymbol()}, 153 * or {@code null} if an invalid currency was set. 154 * @see #setCurrency(Currency) 155 * @see #setInternationalCurrencySymbol(String) 156 */ 157 public Currency getCurrency() { 158 return currency; 159 } 160 161 /** 162 * Returns the international currency symbol. 163 * 164 * @return the international currency symbol as string. 165 */ 166 public String getInternationalCurrencySymbol() { 167 return intlCurrencySymbol; 168 } 169 170 /** 171 * Returns the currency symbol. 172 * 173 * @return the currency symbol as string. 174 */ 175 public String getCurrencySymbol() { 176 return currencySymbol; 177 } 178 179 /** 180 * Returns the character which represents the decimal point in a number. 181 * 182 * @return the decimal separator character. 183 */ 184 public char getDecimalSeparator() { 185 return patternChars[DecimalSeparator]; 186 } 187 188 /** 189 * Returns the character which represents a single digit in a format 190 * pattern. 191 * 192 * @return the digit pattern character. 193 */ 194 public char getDigit() { 195 return patternChars[Digit]; 196 } 197 198 /** 199 * Returns the character used as the thousands separator in a number. 200 * 201 * @return the thousands separator character. 202 */ 203 public char getGroupingSeparator() { 204 return patternChars[GroupingSeparator]; 205 } 206 207 /** 208 * Returns the string which represents infinity. 209 * 210 * @return the infinity symbol as a string. 211 */ 212 public String getInfinity() { 213 return infinity; 214 } 215 216 /** 217 * Returns the minus sign character. 218 * 219 * @return the minus sign as a character. 220 */ 221 public char getMinusSign() { 222 return patternChars[MinusSign]; 223 } 224 225 /** 226 * Returns the character which represents the decimal point in a monetary 227 * value. 228 * 229 * @return the monetary decimal point as a character. 230 */ 231 public char getMonetaryDecimalSeparator() { 232 return patternChars[MonetaryDecimalSeparator]; 233 } 234 235 /** 236 * Returns the string which represents NaN. 237 * 238 * @return the symbol NaN as a string. 239 */ 240 public String getNaN() { 241 return NaN; 242 } 243 244 /** 245 * Returns the character which separates the positive and negative patterns 246 * in a format pattern. 247 * 248 * @return the pattern separator character. 249 */ 250 public char getPatternSeparator() { 251 return patternChars[PatternSeparator]; 252 } 253 254 /** 255 * Returns the percent character. 256 * 257 * @return the percent character. 258 */ 259 public char getPercent() { 260 return patternChars[Percent]; 261 } 262 263 /** 264 * Returns the per mill sign character. 265 * 266 * @return the per mill sign character. 267 */ 268 public char getPerMill() { 269 return patternChars[PerMill]; 270 } 271 272 /** 273 * Returns the character which represents zero. 274 * 275 * @return the zero character. 276 */ 277 public char getZeroDigit() { 278 return patternChars[ZeroDigit]; 279 } 280 281 /* 282 * Returns the exponent as a character. 283 */ 284 char getExponential() { 285 return patternChars[Exponent]; 286 } 287 288 @Override 289 public int hashCode() { 290 return new String(patternChars).hashCode() + infinity.hashCode() 291 + NaN.hashCode() + currencySymbol.hashCode() 292 + intlCurrencySymbol.hashCode(); 293 } 294 295 /** 296 * Sets the currency. 297 * <p> 298 * The international currency symbol and the currency symbol are updated, 299 * but the min and max number of fraction digits stays the same. 300 * <p> 301 * 302 * @param currency 303 * the new currency. 304 * @throws NullPointerException 305 * if {@code currency} is {@code null}. 306 */ 307 public void setCurrency(Currency currency) { 308 if (currency == null) { 309 throw new NullPointerException(); 310 } 311 if (currency == this.currency) { 312 return; 313 } 314 this.currency = currency; 315 intlCurrencySymbol = currency.getCurrencyCode(); 316 currencySymbol = currency.getSymbol(locale); 317 } 318 319 /** 320 * Sets the international currency symbol. 321 * <p> 322 * The currency and currency symbol are also updated if {@code value} is a 323 * valid ISO4217 currency code. 324 * <p> 325 * The min and max number of fraction digits stay the same. 326 * 327 * @param value 328 * the currency code. 329 */ 330 public void setInternationalCurrencySymbol(String value) { 331 if (value == null) { 332 currency = null; 333 intlCurrencySymbol = null; 334 return; 335 } 336 337 if (value.equals(intlCurrencySymbol)) { 338 return; 339 } 340 341 try { 342 currency = Currency.getInstance(value); 343 currencySymbol = currency.getSymbol(locale); 344 } catch (IllegalArgumentException e) { 345 currency = null; 346 } 347 intlCurrencySymbol = value; 348 } 349 350 /** 351 * Sets the currency symbol. 352 * 353 * @param value 354 * the currency symbol. 355 */ 356 public void setCurrencySymbol(String value) { 357 currencySymbol = value; 358 } 359 360 /** 361 * Sets the character which represents the decimal point in a number. 362 * 363 * @param value 364 * the decimal separator character. 365 */ 366 public void setDecimalSeparator(char value) { 367 patternChars[DecimalSeparator] = value; 368 } 369 370 /** 371 * Sets the character which represents a single digit in a format pattern. 372 * 373 * @param value 374 * the digit character. 375 */ 376 public void setDigit(char value) { 377 patternChars[Digit] = value; 378 } 379 380 /** 381 * Sets the character used as the thousands separator in a number. 382 * 383 * @param value 384 * the grouping separator character. 385 */ 386 public void setGroupingSeparator(char value) { 387 patternChars[GroupingSeparator] = value; 388 } 389 390 /** 391 * Sets the string which represents infinity. 392 * 393 * @param value 394 * the string representing infinity. 395 */ 396 public void setInfinity(String value) { 397 infinity = value; 398 } 399 400 /** 401 * Sets the minus sign character. 402 * 403 * @param value 404 * the minus sign character. 405 */ 406 public void setMinusSign(char value) { 407 patternChars[MinusSign] = value; 408 } 409 410 /** 411 * Sets the character which represents the decimal point in a monetary 412 * value. 413 * 414 * @param value 415 * the monetary decimal separator character. 416 */ 417 public void setMonetaryDecimalSeparator(char value) { 418 patternChars[MonetaryDecimalSeparator] = value; 419 } 420 421 /** 422 * Sets the string which represents NaN. 423 * 424 * @param value 425 * the string representing NaN. 426 */ 427 public void setNaN(String value) { 428 NaN = value; 429 } 430 431 /** 432 * Sets the character which separates the positive and negative patterns in 433 * a format pattern. 434 * 435 * @param value 436 * the pattern separator character. 437 */ 438 public void setPatternSeparator(char value) { 439 patternChars[PatternSeparator] = value; 440 } 441 442 /** 443 * Sets the percent character. 444 * 445 * @param value 446 * the percent character. 447 */ 448 public void setPercent(char value) { 449 patternChars[Percent] = value; 450 } 451 452 /** 453 * Sets the per mill sign character. 454 * 455 * @param value 456 * the per mill character. 457 */ 458 public void setPerMill(char value) { 459 patternChars[PerMill] = value; 460 } 461 462 /** 463 * Sets the character which represents zero. 464 * 465 * @param value 466 * the zero digit character. 467 */ 468 public void setZeroDigit(char value) { 469 patternChars[ZeroDigit] = value; 470 } 471 472 /* 473 * Sets the exponent character. 474 */ 475 void setExponential(char value) { 476 patternChars[Exponent] = value; 477 } 478 479 private static final ObjectStreamField[] serialPersistentFields = { 480 new ObjectStreamField("currencySymbol", String.class), //$NON-NLS-1$ 481 new ObjectStreamField("decimalSeparator", Character.TYPE), //$NON-NLS-1$ 482 new ObjectStreamField("digit", Character.TYPE), //$NON-NLS-1$ 483 new ObjectStreamField("exponential", Character.TYPE), //$NON-NLS-1$ 484 new ObjectStreamField("groupingSeparator", Character.TYPE), //$NON-NLS-1$ 485 new ObjectStreamField("infinity", String.class), //$NON-NLS-1$ 486 new ObjectStreamField("intlCurrencySymbol", String.class), //$NON-NLS-1$ 487 new ObjectStreamField("minusSign", Character.TYPE), //$NON-NLS-1$ 488 new ObjectStreamField("monetarySeparator", Character.TYPE), //$NON-NLS-1$ 489 new ObjectStreamField("NaN", String.class), //$NON-NLS-1$ 490 new ObjectStreamField("patternSeparator", Character.TYPE), //$NON-NLS-1$ 491 new ObjectStreamField("percent", Character.TYPE), //$NON-NLS-1$ 492 new ObjectStreamField("perMill", Character.TYPE), //$NON-NLS-1$ 493 new ObjectStreamField("serialVersionOnStream", Integer.TYPE), //$NON-NLS-1$ 494 new ObjectStreamField("zeroDigit", Character.TYPE), //$NON-NLS-1$ 495 new ObjectStreamField("locale", Locale.class), }; //$NON-NLS-1$ 496 497 private void writeObject(ObjectOutputStream stream) throws IOException { 498 ObjectOutputStream.PutField fields = stream.putFields(); 499 fields.put("currencySymbol", currencySymbol); //$NON-NLS-1$ 500 fields.put("decimalSeparator", getDecimalSeparator()); //$NON-NLS-1$ 501 fields.put("digit", getDigit()); //$NON-NLS-1$ 502 fields.put("exponential", getExponential()); //$NON-NLS-1$ 503 fields.put("groupingSeparator", getGroupingSeparator()); //$NON-NLS-1$ 504 fields.put("infinity", infinity); //$NON-NLS-1$ 505 fields.put("intlCurrencySymbol", intlCurrencySymbol); //$NON-NLS-1$ 506 fields.put("minusSign", getMinusSign()); //$NON-NLS-1$ 507 fields.put("monetarySeparator", getMonetaryDecimalSeparator()); //$NON-NLS-1$ 508 fields.put("NaN", NaN); //$NON-NLS-1$ 509 fields.put("patternSeparator", getPatternSeparator()); //$NON-NLS-1$ 510 fields.put("percent", getPercent()); //$NON-NLS-1$ 511 fields.put("perMill", getPerMill()); //$NON-NLS-1$ 512 fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ 513 fields.put("zeroDigit", getZeroDigit()); //$NON-NLS-1$ 514 fields.put("locale", locale); //$NON-NLS-1$ 515 stream.writeFields(); 516 } 517 518 private void readObject(ObjectInputStream stream) throws IOException, 519 ClassNotFoundException { 520 ObjectInputStream.GetField fields = stream.readFields(); 521 patternChars = new char[10]; 522 currencySymbol = (String) fields.get("currencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$ 523 setDecimalSeparator(fields.get("decimalSeparator", '.')); //$NON-NLS-1$ 524 setDigit(fields.get("digit", '#')); //$NON-NLS-1$ 525 setGroupingSeparator(fields.get("groupingSeparator", ',')); //$NON-NLS-1$ 526 infinity = (String) fields.get("infinity", ""); //$NON-NLS-1$ //$NON-NLS-2$ 527 intlCurrencySymbol = (String) fields.get("intlCurrencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$ 528 setMinusSign(fields.get("minusSign", '-')); //$NON-NLS-1$ 529 NaN = (String) fields.get("NaN", ""); //$NON-NLS-1$ //$NON-NLS-2$ 530 setPatternSeparator(fields.get("patternSeparator", ';')); //$NON-NLS-1$ 531 setPercent(fields.get("percent", '%')); //$NON-NLS-1$ 532 setPerMill(fields.get("perMill", '\u2030')); //$NON-NLS-1$ 533 setZeroDigit(fields.get("zeroDigit", '0')); //$NON-NLS-1$ 534 locale = (Locale) fields.get("locale", null); //$NON-NLS-1$ 535 if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$ 536 setMonetaryDecimalSeparator(getDecimalSeparator()); 537 setExponential('E'); 538 } else { 539 setMonetaryDecimalSeparator(fields.get("monetarySeparator", '.')); //$NON-NLS-1$ 540 setExponential(fields.get("exponential", 'E')); //$NON-NLS-1$ 541 542 } 543 try { 544 currency = Currency.getInstance(intlCurrencySymbol); 545 } catch (IllegalArgumentException e) { 546 currency = null; 547 } 548 } 549} 550