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 &lt; a.length; ++i) {
65 *     output.println(nf.format(myNumber[i]) + &quot;; &quot;);
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