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 18// BEGIN android-note 19// changed from ICU to resource bundles 20// END android-note 21 22package java.text; 23 24import java.io.IOException; 25import java.io.InvalidObjectException; 26import java.io.ObjectInputStream; 27import java.io.ObjectOutputStream; 28import java.io.ObjectStreamField; 29import java.math.BigInteger; 30import java.util.Currency; 31import java.util.Locale; 32 33import com.ibm.icu4jni.util.LocaleData; 34import org.apache.harmony.text.internal.nls.Messages; 35 36/** 37 * The abstract base class for all number formats. This class provides the 38 * interface for formatting and parsing numbers. {@code NumberFormat} also 39 * provides methods for determining which locales have number formats, and what 40 * their names are. 41 * <p> 42 * {@code NumberFormat} helps you to format and parse numbers for any locale. 43 * Your code can be completely independent of the locale conventions for decimal 44 * points, thousands-separators, or even the particular decimal digits used, or 45 * whether the number format is even decimal. 46 * <p> 47 * To format a number for the current locale, use one of the factory class 48 * methods: 49 * <blockquote> 50 * 51 * <pre> 52 * myString = NumberFormat.getInstance().format(myNumber); 53 * </pre> 54 * 55 * </blockquote> 56 * <p> 57 * If you are formatting multiple numbers, it is more efficient to get the 58 * format and use it multiple times so that the system doesn't have to fetch the 59 * information about the local language and country conventions multiple times. 60 * <blockquote> 61 * 62 * <pre> 63 * NumberFormat nf = NumberFormat.getInstance(); 64 * for (int i = 0; i < a.length; ++i) { 65 * output.println(nf.format(myNumber[i]) + "; "); 66 * } 67 * </pre> 68 * 69 * </blockquote> 70 * <p> 71 * To format a number for a different locale, specify it in the call to 72 * {@code getInstance}. 73 * <blockquote> 74 * 75 * <pre> 76 * NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH); 77 * </pre> 78 * 79 * </blockquote> 80 * <p> 81 * You can also use a {@code NumberFormat} to parse numbers: 82 * <blockquote> 83 * 84 * <pre> 85 * myNumber = nf.parse(myString); 86 * </pre> 87 * 88 * </blockquote> 89 * <p> 90 * Use {@code getInstance} or {@code getNumberInstance} to get the normal number 91 * format. Use {@code getIntegerInstance} to get an integer number format. Use 92 * {@code getCurrencyInstance} to get the currency number format and use 93 * {@code getPercentInstance} to get a format for displaying percentages. With 94 * this format, a fraction like 0.53 is displayed as 53%. 95 * <p> 96 * You can also control the display of numbers with methods such as 97 * {@code setMinimumFractionDigits}. If you want even more control over the 98 * format or parsing, or want to give your users more control, you can try 99 * casting the {@code NumberFormat} you get from the factory methods to a 100 * {@code DecimalFormat}. This will work for the vast majority of locales; just 101 * remember to put it in a {@code try} block in case you encounter an unusual 102 * one. 103 * <p> 104 * {@code NumberFormat} is designed such that some controls work for formatting 105 * and others work for parsing. For example, {@code setParseIntegerOnly} only 106 * affects parsing: If set to {@code true}, "3456.78" is parsed as 3456 (and 107 * leaves the parse position just after '6'); if set to {@code false}, 108 * "3456.78" is parsed as 3456.78 (and leaves the parse position just after 109 * '8'). This is independent of formatting. 110 * <p> 111 * You can also use forms of the {@code parse} and {@code format} methods with 112 * {@code ParsePosition} and {@code FieldPosition} to allow you to: 113 * <ul> 114 * <li>progressively parse through pieces of a string;</li> 115 * <li>align the decimal point and other areas.</li> 116 * </ul> 117 * For example, you can align numbers in two ways: 118 * <ol> 119 * <li> If you are using a monospaced font with spacing for alignment, you can 120 * pass the {@code FieldPosition} in your format call, with {@code field} = 121 * {@code INTEGER_FIELD}. On output, {@code getEndIndex} will be set to the 122 * offset between the last character of the integer and the decimal. Add 123 * (desiredSpaceCount - getEndIndex) spaces to the front of the string.</li> 124 * <li> If you are using proportional fonts, instead of padding with spaces, 125 * measure the width of the string in pixels from the start to 126 * {@code getEndIndex}. Then move the pen by (desiredPixelWidth - 127 * widthToAlignmentPoint) before drawing the text. This also works where there 128 * is no decimal but possibly additional characters before or after the number, 129 * for example with parentheses in negative numbers: "(12)" for -12.</li> 130 * </ol> 131 * <h4>Synchronization</h4> 132 * <p> 133 * Number formats are generally not synchronized. It is recommended to create 134 * separate format instances for each thread. If multiple threads access a 135 * format concurrently, it must be synchronized externally. 136 * <p> 137 * <h4>DecimalFormat</h4> 138 * <p> 139 * {@code DecimalFormat} is the concrete implementation of {@code NumberFormat}, 140 * and the {@code NumberFormat} API is essentially an abstraction of 141 * {@code DecimalFormat's} API. Refer to {@code DecimalFormat} for more 142 * information about this API. 143 * 144 * @see DecimalFormat 145 * @see java.text.ChoiceFormat 146 */ 147public abstract class NumberFormat extends Format { 148 149 private static final long serialVersionUID = -2308460125733713944L; 150 151 /** 152 * Field constant identifying the integer part of a number. 153 */ 154 public static final int INTEGER_FIELD = 0; 155 156 /** 157 * Field constant identifying the fractional part of a number. 158 */ 159 public static final int FRACTION_FIELD = 1; 160 161 private boolean groupingUsed = true, parseIntegerOnly = false; 162 163 private int maximumIntegerDigits = 40, minimumIntegerDigits = 1, 164 maximumFractionDigits = 3, minimumFractionDigits = 0; 165 166 /** 167 * Constructs a new instance of {@code NumberFormat}. 168 */ 169 public NumberFormat() { 170 } 171 172 /** 173 * Returns a new {@code NumberFormat} with the same properties as this 174 * {@code NumberFormat}. 175 * 176 * @return a shallow copy of this {@code NumberFormat}. 177 * @see java.lang.Cloneable 178 */ 179 @Override 180 public Object clone() { 181 return super.clone(); 182 } 183 184 /** 185 * Compares the specified object to this number format and indicates if 186 * they are equal. In order to be equal, {@code object} must be an instance 187 * of {@code NumberFormat} with the same pattern and properties. 188 * 189 * @param object 190 * the object to compare with this object. 191 * @return {@code true} if the specified object is equal to this number 192 * format; {@code false} otherwise. 193 * @see #hashCode 194 */ 195 @Override 196 public boolean equals(Object object) { 197 if (object == this) { 198 return true; 199 } 200 if (!(object instanceof NumberFormat)) { 201 return false; 202 } 203 NumberFormat obj = (NumberFormat) object; 204 return groupingUsed == obj.groupingUsed 205 && parseIntegerOnly == obj.parseIntegerOnly 206 && maximumFractionDigits == obj.maximumFractionDigits 207 && maximumIntegerDigits == obj.maximumIntegerDigits 208 && minimumFractionDigits == obj.minimumFractionDigits 209 && minimumIntegerDigits == obj.minimumIntegerDigits; 210 } 211 212 /** 213 * Formats the specified double using the rules of this number format. 214 * 215 * @param value 216 * the double to format. 217 * @return the formatted string. 218 */ 219 public final String format(double value) { 220 return format(value, new StringBuffer(), new FieldPosition(0)) 221 .toString(); 222 } 223 224 /** 225 * Formats the specified double value as a string using the pattern of this 226 * number format and appends the string to the specified string buffer. 227 * <p> 228 * If the {@code field} member of {@code position} contains a value 229 * specifying a format field, then its {@code beginIndex} and 230 * {@code endIndex} members will be updated with the position of the first 231 * occurrence of this field in the formatted text. 232 * 233 * @param value 234 * the double to format. 235 * @param buffer 236 * the target string buffer to append the formatted double value 237 * to. 238 * @param field 239 * on input: an optional alignment field; on output: the offsets 240 * of the alignment field in the formatted text. 241 * @return the string buffer. 242 */ 243 public abstract StringBuffer format(double value, StringBuffer buffer, FieldPosition field); 244 245 /** 246 * Formats the specified long using the rules of this number format. 247 * 248 * @param value 249 * the long to format. 250 * @return the formatted string. 251 */ 252 public final String format(long value) { 253 return format(value, new StringBuffer(), new FieldPosition(0)) 254 .toString(); 255 } 256 257 /** 258 * Formats the specified long value as a string using the pattern of this 259 * number format and appends the string to the specified string buffer. 260 * <p> 261 * If the {@code field} member of {@code position} contains a value 262 * specifying a format field, then its {@code beginIndex} and 263 * {@code endIndex} members will be updated with the position of the first 264 * occurrence of this field in the formatted text. 265 * 266 * @param value 267 * the long to format. 268 * @param buffer 269 * the target string buffer to append the formatted long value 270 * to. 271 * @param field 272 * on input: an optional alignment field; on output: the offsets 273 * of the alignment field in the formatted text. 274 * @return the string buffer. 275 */ 276 public abstract StringBuffer format(long value, StringBuffer buffer, FieldPosition field); 277 278 /** 279 * Formats a number into a supplied buffer. 280 * <p> 281 * The number must be a subclass of {@code Number}. Instances of {@code Byte}, {@code Short}, 282 * {@code Integer}, and {@code Long} have {@code Number.longValue} invoked, as do instances of 283 * {@code BigInteger} where {@code BigInteger.bitLength} returns <i>less than</i> 64. All other 284 * values have {@code Number.doubleValue} invoked instead. 285 * <p> 286 * If the {@code field} member of {@code field} contains a value specifying 287 * a format field, then its {@code beginIndex} and {@code endIndex} members 288 * will be updated with the position of the first occurrence of this field 289 * in the formatted text. 290 * 291 * @param object 292 * the object to format, must be a {@code Number}. 293 * @param buffer 294 * the target string buffer to append the formatted number to. 295 * @param field 296 * on input: an optional alignment field; on output: the offsets 297 * of the alignment field in the formatted text. 298 * @return the string buffer. 299 * @throws IllegalArgumentException 300 * if {@code object} is not an instance of {@code Number}. 301 */ 302 @Override 303 public StringBuffer format(Object object, StringBuffer buffer, FieldPosition field) { 304 if (object instanceof Byte || object instanceof Short || object instanceof Integer || 305 object instanceof Long || 306 (object instanceof BigInteger && ((BigInteger) object).bitLength() < 64)) { 307 long lv = ((Number) object).longValue(); 308 return format(lv, buffer, field); 309 } else if (object instanceof Number) { 310 double dv = ((Number) object).doubleValue(); 311 return format(dv, buffer, field); 312 } 313 throw new IllegalArgumentException(); 314 } 315 316 /** 317 * Gets the list of installed locales which support {@code NumberFormat}. 318 * 319 * @return an array of locales. 320 */ 321 public static Locale[] getAvailableLocales() { 322 return Locale.getAvailableLocales(); 323 } 324 325 /** 326 * Returns the currency used by this number format. 327 * <p> 328 * This implementation throws {@code UnsupportedOperationException}, 329 * concrete subclasses should override this method if they support currency 330 * formatting. 331 * <p> 332 * 333 * @return the currency that was set in getInstance() or in setCurrency(), 334 * or {@code null}. 335 * @throws UnsupportedOperationException 336 */ 337 public Currency getCurrency() { 338 throw new UnsupportedOperationException(); 339 } 340 341 /** 342 * Returns a {@code NumberFormat} for formatting and parsing currency values 343 * for the default locale. 344 * 345 * @return a {@code NumberFormat} for handling currency values. 346 */ 347 public final static NumberFormat getCurrencyInstance() { 348 return getCurrencyInstance(Locale.getDefault()); 349 } 350 351 /** 352 * Returns a {@code NumberFormat} for formatting and parsing currency values 353 * for the specified locale. 354 * 355 * @param locale 356 * the locale to use. 357 * @return a {@code NumberFormat} for handling currency values. 358 */ 359 public static NumberFormat getCurrencyInstance(Locale locale) { 360 // BEGIN android-changed 361 LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); 362 return getInstance(localeData.currencyPattern, locale); 363 // END android-changed 364 } 365 366 /** 367 * Returns a {@code NumberFormat} for formatting and parsing integers for the 368 * default locale. 369 * 370 * @return a {@code NumberFormat} for handling integers. 371 */ 372 public final static NumberFormat getIntegerInstance() { 373 return getIntegerInstance(Locale.getDefault()); 374 } 375 376 /** 377 * Returns a {@code NumberFormat} for formatting and parsing integers for 378 * the specified locale. 379 * 380 * @param locale 381 * the locale to use. 382 * @return a {@code NumberFormat} for handling integers. 383 */ 384 public static NumberFormat getIntegerInstance(Locale locale) { 385 // BEGIN android-changed 386 LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); 387 NumberFormat result = getInstance(localeData.integerPattern, locale); 388 result.setParseIntegerOnly(true); 389 return result; 390 // END android-changed 391 } 392 393 /** 394 * Returns a {@code NumberFormat} for formatting and parsing numbers for the 395 * default locale. 396 * 397 * @return a {@code NumberFormat} for handling {@code Number} objects. 398 */ 399 public final static NumberFormat getInstance() { 400 return getNumberInstance(); 401 } 402 403 /** 404 * Returns a {@code NumberFormat} for formatting and parsing numbers for the 405 * specified locale. 406 * 407 * @param locale 408 * the locale to use. 409 * @return a {@code NumberFormat} for handling {@code Number} objects. 410 */ 411 public static NumberFormat getInstance(Locale locale) { 412 return getNumberInstance(locale); 413 } 414 415 // BEGIN android-added 416 private static NumberFormat getInstance(String pattern, Locale locale) { 417 return new DecimalFormat(pattern, locale); 418 } 419 // END android-added 420 421 /** 422 * Returns the maximum number of fraction digits that are printed when 423 * formatting. If the maximum is less than the number of fraction digits, 424 * the least significant digits are truncated. 425 * 426 * @return the maximum number of fraction digits. 427 */ 428 public int getMaximumFractionDigits() { 429 return maximumFractionDigits; 430 } 431 432 /** 433 * Returns the maximum number of integer digits that are printed when 434 * formatting. If the maximum is less than the number of integer digits, the 435 * most significant digits are truncated. 436 * 437 * @return the maximum number of integer digits. 438 */ 439 public int getMaximumIntegerDigits() { 440 return maximumIntegerDigits; 441 } 442 443 /** 444 * Returns the minimum number of fraction digits that are printed when 445 * formatting. 446 * 447 * @return the minimum number of fraction digits. 448 */ 449 public int getMinimumFractionDigits() { 450 return minimumFractionDigits; 451 } 452 453 /** 454 * Returns the minimum number of integer digits that are printed when 455 * formatting. 456 * 457 * @return the minimum number of integer digits. 458 */ 459 public int getMinimumIntegerDigits() { 460 return minimumIntegerDigits; 461 } 462 463 /** 464 * Returns a {@code NumberFormat} for formatting and parsing numbers for the 465 * default locale. 466 * 467 * @return a {@code NumberFormat} for handling {@code Number} objects. 468 */ 469 public final static NumberFormat getNumberInstance() { 470 return getNumberInstance(Locale.getDefault()); 471 } 472 473 /** 474 * Returns a {@code NumberFormat} for formatting and parsing numbers for the 475 * specified locale. 476 * 477 * @param locale 478 * the locale to use. 479 * @return a {@code NumberFormat} for handling {@code Number} objects. 480 */ 481 public static NumberFormat getNumberInstance(Locale locale) { 482 // BEGIN android-changed 483 LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); 484 return getInstance(localeData.numberPattern, locale); 485 // END android-changed 486 } 487 488 /** 489 * Returns a {@code NumberFormat} for formatting and parsing percentage 490 * values for the default locale. 491 * 492 * @return a {@code NumberFormat} for handling percentage values. 493 */ 494 public final static NumberFormat getPercentInstance() { 495 return getPercentInstance(Locale.getDefault()); 496 } 497 498 /** 499 * Returns a {@code NumberFormat} for formatting and parsing percentage 500 * values for the specified locale. 501 * 502 * @param locale 503 * the locale to use. 504 * @return a {@code NumberFormat} for handling percentage values. 505 */ 506 public static NumberFormat getPercentInstance(Locale locale) { 507 // BEGIN android-changed 508 LocaleData localeData = com.ibm.icu4jni.util.Resources.getLocaleData(locale); 509 return getInstance(localeData.percentPattern, locale); 510 // END android-changed 511 } 512 513 @Override 514 public int hashCode() { 515 return (groupingUsed ? 1231 : 1237) + (parseIntegerOnly ? 1231 : 1237) 516 + maximumFractionDigits + maximumIntegerDigits 517 + minimumFractionDigits + minimumIntegerDigits; 518 } 519 520 /** 521 * Indicates whether this number format formats and parses numbers using a 522 * grouping separator. 523 * 524 * @return {@code true} if a grouping separator is used; {@code false} 525 * otherwise. 526 */ 527 public boolean isGroupingUsed() { 528 return groupingUsed; 529 } 530 531 /** 532 * Indicates whether this number format only parses integer numbers. Parsing 533 * stops if a decimal separator is encountered. 534 * 535 * @return {@code true} if this number format only parses integers, 536 * {@code false} if if parsese integers as well as fractions. 537 */ 538 public boolean isParseIntegerOnly() { 539 return parseIntegerOnly; 540 } 541 542 /** 543 * Parses a {@code Number} from the specified string using the rules of this 544 * number format. 545 * 546 * @param string 547 * the string to parse. 548 * @return the {@code Number} resulting from the parsing. 549 * @throws ParseException 550 * if an error occurs during parsing. 551 */ 552 public Number parse(String string) throws ParseException { 553 ParsePosition pos = new ParsePosition(0); 554 Number number = parse(string, pos); 555 if (pos.getIndex() == 0) { 556 // text.1D=Unparseable number: {0} 557 throw new ParseException( 558 Messages.getString("text.1D", string), pos.getErrorIndex()); //$NON-NLS-1$ 559 } 560 return number; 561 } 562 563 /** 564 * Parses a {@code Number} from the specified string starting at the index 565 * specified by {@code position}. If the string is successfully parsed then 566 * the index of the {@code ParsePosition} is updated to the index following 567 * the parsed text. On error, the index is unchanged and the error index of 568 * {@code ParsePosition} is set to the index where the error occurred. 569 * 570 * @param string 571 * the string to parse. 572 * @param position 573 * input/output parameter, specifies the start index in 574 * {@code string} from where to start parsing. If parsing is 575 * successful, it is updated with the index following the parsed 576 * text; on error, the index is unchanged and the error index is 577 * set to the index where the error occurred. 578 * @return the {@code Number} resulting from the parse or {@code null} if 579 * there is an error. 580 */ 581 public abstract Number parse(String string, ParsePosition position); 582 583 @Override 584 public final Object parseObject(String string, ParsePosition position) { 585 if (position == null) { 586 // text.1A=position is null 587 throw new NullPointerException(Messages.getString("text.1A")); //$NON-NLS-1$ 588 } 589 590 try { 591 return parse(string, position); 592 } catch (Exception e) { 593 return null; 594 } 595 } 596 597 /** 598 * Sets the currency used by this number format when formatting currency 599 * values. The min and max fraction digits remain the same. 600 * <p> 601 * This implementation throws {@code UnsupportedOperationException}, 602 * concrete subclasses should override this method if they support currency 603 * formatting. 604 * 605 * @param currency 606 * the new currency. 607 * @throws UnsupportedOperationException 608 */ 609 public void setCurrency(Currency currency) { 610 throw new UnsupportedOperationException(); 611 } 612 613 /** 614 * Sets whether this number format formats and parses numbers using a 615 * grouping separator. 616 * 617 * @param value 618 * {@code true} if a grouping separator is used; {@code false} 619 * otherwise. 620 */ 621 public void setGroupingUsed(boolean value) { 622 groupingUsed = value; 623 } 624 625 /** 626 * Sets the maximum number of fraction digits that are printed when 627 * formatting. If the maximum is less than the number of fraction digits, 628 * the least significant digits are truncated. 629 * 630 * @param value 631 * the maximum number of fraction digits. 632 */ 633 public void setMaximumFractionDigits(int value) { 634 maximumFractionDigits = value < 0 ? 0 : value; 635 if (maximumFractionDigits < minimumFractionDigits) { 636 minimumFractionDigits = maximumFractionDigits; 637 } 638 } 639 640 /** 641 * Sets the new maximum count of integer digits that are printed when 642 * formatting. If the maximum is less than the number of integer digits, the 643 * most significant digits are truncated. 644 * 645 * @param value 646 * the new maximum number of integer numerals for display. 647 */ 648 public void setMaximumIntegerDigits(int value) { 649 maximumIntegerDigits = value < 0 ? 0 : value; 650 if (maximumIntegerDigits < minimumIntegerDigits) { 651 minimumIntegerDigits = maximumIntegerDigits; 652 } 653 } 654 655 /** 656 * Sets the minimum number of fraction digits that are printed when 657 * formatting. 658 * 659 * @param value 660 * the minimum number of fraction digits. 661 */ 662 public void setMinimumFractionDigits(int value) { 663 minimumFractionDigits = value < 0 ? 0 : value; 664 if (maximumFractionDigits < minimumFractionDigits) { 665 maximumFractionDigits = minimumFractionDigits; 666 } 667 } 668 669 /** 670 * Sets the minimum number of integer digits that are printed when 671 * formatting. 672 * 673 * @param value 674 * the minimum number of integer digits. 675 */ 676 public void setMinimumIntegerDigits(int value) { 677 minimumIntegerDigits = value < 0 ? 0 : value; 678 if (maximumIntegerDigits < minimumIntegerDigits) { 679 maximumIntegerDigits = minimumIntegerDigits; 680 } 681 } 682 683 /** 684 * Specifies if this number format should parse numbers only as integers or 685 * else as any kind of number. If this method is called with a {@code true} 686 * value then subsequent parsing attempts will stop if a decimal separator 687 * is encountered. 688 * 689 * @param value 690 * {@code true} to only parse integers, {@code false} to parse 691 * integers as well as fractions. 692 */ 693 public void setParseIntegerOnly(boolean value) { 694 parseIntegerOnly = value; 695 } 696 697 private static final ObjectStreamField[] serialPersistentFields = { 698 new ObjectStreamField("groupingUsed", Boolean.TYPE), //$NON-NLS-1$ 699 new ObjectStreamField("maxFractionDigits", Byte.TYPE), //$NON-NLS-1$ 700 new ObjectStreamField("maximumFractionDigits", Integer.TYPE), //$NON-NLS-1$ 701 new ObjectStreamField("maximumIntegerDigits", Integer.TYPE), //$NON-NLS-1$ 702 new ObjectStreamField("maxIntegerDigits", Byte.TYPE), //$NON-NLS-1$ 703 new ObjectStreamField("minFractionDigits", Byte.TYPE), //$NON-NLS-1$ 704 new ObjectStreamField("minimumFractionDigits", Integer.TYPE), //$NON-NLS-1$ 705 new ObjectStreamField("minimumIntegerDigits", Integer.TYPE), //$NON-NLS-1$ 706 new ObjectStreamField("minIntegerDigits", Byte.TYPE), //$NON-NLS-1$ 707 new ObjectStreamField("parseIntegerOnly", Boolean.TYPE), //$NON-NLS-1$ 708 new ObjectStreamField("serialVersionOnStream", Integer.TYPE), }; //$NON-NLS-1$ 709 710 private void writeObject(ObjectOutputStream stream) throws IOException { 711 ObjectOutputStream.PutField fields = stream.putFields(); 712 fields.put("groupingUsed", groupingUsed); //$NON-NLS-1$ 713 fields 714 .put( 715 "maxFractionDigits", //$NON-NLS-1$ 716 maximumFractionDigits < Byte.MAX_VALUE ? (byte) maximumFractionDigits 717 : Byte.MAX_VALUE); 718 fields.put("maximumFractionDigits", maximumFractionDigits); //$NON-NLS-1$ 719 fields.put("maximumIntegerDigits", maximumIntegerDigits); //$NON-NLS-1$ 720 fields 721 .put( 722 "maxIntegerDigits", //$NON-NLS-1$ 723 maximumIntegerDigits < Byte.MAX_VALUE ? (byte) maximumIntegerDigits 724 : Byte.MAX_VALUE); 725 fields 726 .put( 727 "minFractionDigits", //$NON-NLS-1$ 728 minimumFractionDigits < Byte.MAX_VALUE ? (byte) minimumFractionDigits 729 : Byte.MAX_VALUE); 730 fields.put("minimumFractionDigits", minimumFractionDigits); //$NON-NLS-1$ 731 fields.put("minimumIntegerDigits", minimumIntegerDigits); //$NON-NLS-1$ 732 fields 733 .put( 734 "minIntegerDigits", //$NON-NLS-1$ 735 minimumIntegerDigits < Byte.MAX_VALUE ? (byte) minimumIntegerDigits 736 : Byte.MAX_VALUE); 737 fields.put("parseIntegerOnly", parseIntegerOnly); //$NON-NLS-1$ 738 fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ 739 stream.writeFields(); 740 } 741 742 private void readObject(ObjectInputStream stream) throws IOException, 743 ClassNotFoundException { 744 ObjectInputStream.GetField fields = stream.readFields(); 745 groupingUsed = fields.get("groupingUsed", true); //$NON-NLS-1$ 746 parseIntegerOnly = fields.get("parseIntegerOnly", false); //$NON-NLS-1$ 747 if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$ 748 maximumFractionDigits = fields.get("maxFractionDigits", (byte) 3); //$NON-NLS-1$ 749 maximumIntegerDigits = fields.get("maxIntegerDigits", (byte) 40); //$NON-NLS-1$ 750 minimumFractionDigits = fields.get("minFractionDigits", (byte) 0); //$NON-NLS-1$ 751 minimumIntegerDigits = fields.get("minIntegerDigits", (byte) 1); //$NON-NLS-1$ 752 } else { 753 maximumFractionDigits = fields.get("maximumFractionDigits", 3); //$NON-NLS-1$ 754 maximumIntegerDigits = fields.get("maximumIntegerDigits", 40); //$NON-NLS-1$ 755 minimumFractionDigits = fields.get("minimumFractionDigits", 0); //$NON-NLS-1$ 756 minimumIntegerDigits = fields.get("minimumIntegerDigits", 1); //$NON-NLS-1$ 757 } 758 if (minimumIntegerDigits > maximumIntegerDigits 759 || minimumFractionDigits > maximumFractionDigits) { 760 // text.00=min digits greater than max digits 761 throw new InvalidObjectException(Messages.getString("text.00")); //$NON-NLS-1$ 762 } 763 if (minimumIntegerDigits < 0 || maximumIntegerDigits < 0 764 || minimumFractionDigits < 0 || maximumFractionDigits < 0) { 765 // text.01=min or max digits negative 766 throw new InvalidObjectException(Messages.getString("text.01")); //$NON-NLS-1$ 767 } 768 } 769 770 /** 771 * The instances of this inner class are used as attribute keys and values 772 * in {@code AttributedCharacterIterator} that the 773 * {@link NumberFormat#formatToCharacterIterator(Object)} method returns. 774 * <p> 775 * There is no public constructor in this class, the only instances are the 776 * constants defined here. 777 * <p> 778 */ 779 public static class Field extends Format.Field { 780 781 private static final long serialVersionUID = 7494728892700160890L; 782 783 /** 784 * This constant stands for the number sign. 785 */ 786 public static final Field SIGN = new Field("sign"); //$NON-NLS-1$ 787 788 /** 789 * This constant stands for the integer part of the number. 790 */ 791 public static final Field INTEGER = new Field("integer"); //$NON-NLS-1$ 792 793 /** 794 * This constant stands for the fraction part of the number. 795 */ 796 public static final Field FRACTION = new Field("fraction"); //$NON-NLS-1$ 797 798 /** 799 * This constant stands for the exponent part of the number. 800 */ 801 public static final Field EXPONENT = new Field("exponent"); //$NON-NLS-1$ 802 803 /** 804 * This constant stands for the exponent sign symbol. 805 */ 806 public static final Field EXPONENT_SIGN = new Field("exponent sign"); //$NON-NLS-1$ 807 808 /** 809 * This constant stands for the exponent symbol. 810 */ 811 public static final Field EXPONENT_SYMBOL = new Field("exponent symbol"); //$NON-NLS-1$ 812 813 /** 814 * This constant stands for the decimal separator. 815 */ 816 public static final Field DECIMAL_SEPARATOR = new Field( 817 "decimal separator"); //$NON-NLS-1$ 818 819 /** 820 * This constant stands for the grouping separator. 821 */ 822 public static final Field GROUPING_SEPARATOR = new Field( 823 "grouping separator"); //$NON-NLS-1$ 824 825 /** 826 * This constant stands for the percent symbol. 827 */ 828 public static final Field PERCENT = new Field("percent"); //$NON-NLS-1$ 829 830 /** 831 * This constant stands for the permille symbol. 832 */ 833 public static final Field PERMILLE = new Field("per mille"); //$NON-NLS-1$ 834 835 /** 836 * This constant stands for the currency symbol. 837 */ 838 public static final Field CURRENCY = new Field("currency"); //$NON-NLS-1$ 839 840 /** 841 * Constructs a new instance of {@code NumberFormat.Field} with the 842 * given field name. 843 * 844 * @param fieldName 845 * the field name. 846 */ 847 protected Field(String fieldName) { 848 super(fieldName); 849 } 850 851 /** 852 * Resolves instances that are deserialized to the constant 853 * {@code NumberFormat.Field} values. 854 * 855 * @return the resolved field object. 856 * @throws InvalidObjectException 857 * if an error occurs while resolving the field object. 858 */ 859 @Override 860 protected Object readResolve() throws InvalidObjectException { 861 if (this.equals(INTEGER)) { 862 return INTEGER; 863 } 864 if (this.equals(FRACTION)) { 865 return FRACTION; 866 } 867 if (this.equals(EXPONENT)) { 868 return EXPONENT; 869 } 870 if (this.equals(EXPONENT_SIGN)) { 871 return EXPONENT_SIGN; 872 } 873 if (this.equals(EXPONENT_SYMBOL)) { 874 return EXPONENT_SYMBOL; 875 } 876 if (this.equals(CURRENCY)) { 877 return CURRENCY; 878 } 879 if (this.equals(DECIMAL_SEPARATOR)) { 880 return DECIMAL_SEPARATOR; 881 } 882 if (this.equals(GROUPING_SEPARATOR)) { 883 return GROUPING_SEPARATOR; 884 } 885 if (this.equals(PERCENT)) { 886 return PERCENT; 887 } 888 if (this.equals(PERMILLE)) { 889 return PERMILLE; 890 } 891 if (this.equals(SIGN)) { 892 return SIGN; 893 } 894 // text.02=Unknown attribute 895 throw new InvalidObjectException(Messages.getString("text.02")); //$NON-NLS-1$ 896 } 897 } 898 899} 900