• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.icu;
18 
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.math.RoundingMode;
22 import java.text.AttributedCharacterIterator;
23 import java.text.AttributedString;
24 import java.text.DecimalFormatSymbols;
25 import java.text.FieldPosition;
26 import java.text.Format;
27 import java.text.NumberFormat;
28 import java.text.ParsePosition;
29 import java.util.Currency;
30 import java.util.NoSuchElementException;
31 
32 public final class NativeDecimalFormat {
33     /**
34      * Constants corresponding to the native type UNumberFormatSymbol, for setSymbol.
35      */
36     private static final int UNUM_DECIMAL_SEPARATOR_SYMBOL = 0;
37     private static final int UNUM_GROUPING_SEPARATOR_SYMBOL = 1;
38     private static final int UNUM_PATTERN_SEPARATOR_SYMBOL = 2;
39     private static final int UNUM_PERCENT_SYMBOL = 3;
40     private static final int UNUM_ZERO_DIGIT_SYMBOL = 4;
41     private static final int UNUM_DIGIT_SYMBOL = 5;
42     private static final int UNUM_MINUS_SIGN_SYMBOL = 6;
43     private static final int UNUM_PLUS_SIGN_SYMBOL = 7;
44     private static final int UNUM_CURRENCY_SYMBOL = 8;
45     private static final int UNUM_INTL_CURRENCY_SYMBOL = 9;
46     private static final int UNUM_MONETARY_SEPARATOR_SYMBOL = 10;
47     private static final int UNUM_EXPONENTIAL_SYMBOL = 11;
48     private static final int UNUM_PERMILL_SYMBOL = 12;
49     private static final int UNUM_PAD_ESCAPE_SYMBOL = 13;
50     private static final int UNUM_INFINITY_SYMBOL = 14;
51     private static final int UNUM_NAN_SYMBOL = 15;
52     private static final int UNUM_SIGNIFICANT_DIGIT_SYMBOL = 16;
53     private static final int UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL = 17;
54     private static final int UNUM_FORMAT_SYMBOL_COUNT = 18;
55 
56     /**
57      * Constants corresponding to the native type UNumberFormatAttribute, for
58      * getAttribute/setAttribute.
59      */
60     private static final int UNUM_PARSE_INT_ONLY = 0;
61     private static final int UNUM_GROUPING_USED = 1;
62     private static final int UNUM_DECIMAL_ALWAYS_SHOWN = 2;
63     private static final int UNUM_MAX_INTEGER_DIGITS = 3;
64     private static final int UNUM_MIN_INTEGER_DIGITS = 4;
65     private static final int UNUM_INTEGER_DIGITS = 5;
66     private static final int UNUM_MAX_FRACTION_DIGITS = 6;
67     private static final int UNUM_MIN_FRACTION_DIGITS = 7;
68     private static final int UNUM_FRACTION_DIGITS = 8;
69     private static final int UNUM_MULTIPLIER = 9;
70     private static final int UNUM_GROUPING_SIZE = 10;
71     private static final int UNUM_ROUNDING_MODE = 11;
72     private static final int UNUM_ROUNDING_INCREMENT = 12;
73     private static final int UNUM_FORMAT_WIDTH = 13;
74     private static final int UNUM_PADDING_POSITION = 14;
75     private static final int UNUM_SECONDARY_GROUPING_SIZE = 15;
76     private static final int UNUM_SIGNIFICANT_DIGITS_USED = 16;
77     private static final int UNUM_MIN_SIGNIFICANT_DIGITS = 17;
78     private static final int UNUM_MAX_SIGNIFICANT_DIGITS = 18;
79     private static final int UNUM_LENIENT_PARSE = 19;
80 
81     /**
82      * Constants corresponding to the native type UNumberFormatTextAttribute, for
83      * getTextAttribute/setTextAttribute.
84      */
85     private static final int UNUM_POSITIVE_PREFIX = 0;
86     private static final int UNUM_POSITIVE_SUFFIX = 1;
87     private static final int UNUM_NEGATIVE_PREFIX = 2;
88     private static final int UNUM_NEGATIVE_SUFFIX = 3;
89     private static final int UNUM_PADDING_CHARACTER = 4;
90     private static final int UNUM_CURRENCY_CODE = 5;
91     private static final int UNUM_DEFAULT_RULESET = 6;
92     private static final int UNUM_PUBLIC_RULESETS = 7;
93 
94     /**
95      * The address of the ICU DecimalFormat* on the native heap.
96      */
97     private int address;
98 
99     /**
100      * The last pattern we gave to ICU, so we can make repeated applications cheap.
101      * This helps in cases like String.format("%.2f,%.2f\n", x, y) where the DecimalFormat is
102      * reused.
103      */
104     private String lastPattern;
105 
106     // TODO: store all these in DecimalFormat instead!
107     private boolean negPrefNull;
108     private boolean negSuffNull;
109     private boolean posPrefNull;
110     private boolean posSuffNull;
111 
112     private transient boolean parseBigDecimal;
113 
114     /**
115      * Cache the BigDecimal form of the multiplier. This is null until we've
116      * formatted a BigDecimal (with a multiplier that is not 1), or the user has
117      * explicitly called {@link #setMultiplier(int)} with any multiplier.
118      */
119     private BigDecimal multiplierBigDecimal = null;
120 
NativeDecimalFormat(String pattern, DecimalFormatSymbols dfs)121     public NativeDecimalFormat(String pattern, DecimalFormatSymbols dfs) {
122         try {
123             this.address = open(pattern, dfs.getCurrencySymbol(),
124                     dfs.getDecimalSeparator(), dfs.getDigit(), dfs.getExponentSeparator(),
125                     dfs.getGroupingSeparator(), dfs.getInfinity(),
126                     dfs.getInternationalCurrencySymbol(), dfs.getMinusSign(),
127                     dfs.getMonetaryDecimalSeparator(), dfs.getNaN(), dfs.getPatternSeparator(),
128                     dfs.getPercent(), dfs.getPerMill(), dfs.getZeroDigit());
129             this.lastPattern = pattern;
130         } catch (NullPointerException npe) {
131             throw npe;
132         } catch (RuntimeException re) {
133             throw new IllegalArgumentException("syntax error: " + re.getMessage() + ": " + pattern);
134         }
135     }
136 
137     // Used so java.util.Formatter doesn't need to allocate DecimalFormatSymbols instances.
NativeDecimalFormat(String pattern, LocaleData data)138     public NativeDecimalFormat(String pattern, LocaleData data) {
139         this.address = open(pattern, data.currencySymbol,
140                 data.decimalSeparator, '#', data.exponentSeparator, data.groupingSeparator,
141                 data.infinity, data.internationalCurrencySymbol, data.minusSign,
142                 data.monetarySeparator, data.NaN, data.patternSeparator,
143                 data.percent, data.perMill, data.zeroDigit);
144         this.lastPattern = pattern;
145     }
146 
147     // Used to implement clone.
NativeDecimalFormat(NativeDecimalFormat other)148     private NativeDecimalFormat(NativeDecimalFormat other) {
149         this.address = cloneImpl(other.address);
150         this.lastPattern = other.lastPattern;
151         this.negPrefNull = other.negPrefNull;
152         this.negSuffNull = other.negSuffNull;
153         this.posPrefNull = other.posPrefNull;
154         this.posSuffNull = other.posSuffNull;
155     }
156 
157     // TODO: remove this and just have DecimalFormat.hashCode do the right thing itself.
158     @Override
hashCode()159     public int hashCode() {
160         return this.getPositivePrefix().hashCode();
161     }
162 
close()163     public synchronized void close() {
164         if (address != 0) {
165             close(address);
166             address = 0;
167         }
168     }
169 
170     @Override
clone()171     public Object clone() {
172         return new NativeDecimalFormat(this);
173     }
174 
175     /**
176      * Note: this doesn't check that the underlying native DecimalFormat objects' configured
177      * native DecimalFormatSymbols objects are equal. It is assumed that the
178      * caller (DecimalFormat) will check the DecimalFormatSymbols objects
179      * instead, for performance.
180      *
181      * This is also unreasonably expensive, calling down to JNI multiple times.
182      *
183      * TODO: remove this and just have DecimalFormat.equals do the right thing itself.
184      */
185     @Override
equals(Object object)186     public boolean equals(Object object) {
187         if (object == this) {
188             return true;
189         }
190         if (!(object instanceof NativeDecimalFormat)) {
191             return false;
192         }
193         NativeDecimalFormat obj = (NativeDecimalFormat) object;
194         if (obj.address == this.address) {
195             return true;
196         }
197         return obj.toPattern().equals(this.toPattern()) &&
198                 obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown() &&
199                 obj.getGroupingSize() == this.getGroupingSize() &&
200                 obj.getMultiplier() == this.getMultiplier() &&
201                 obj.getNegativePrefix().equals(this.getNegativePrefix()) &&
202                 obj.getNegativeSuffix().equals(this.getNegativeSuffix()) &&
203                 obj.getPositivePrefix().equals(this.getPositivePrefix()) &&
204                 obj.getPositiveSuffix().equals(this.getPositiveSuffix()) &&
205                 obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits() &&
206                 obj.getMaximumFractionDigits() == this.getMaximumFractionDigits() &&
207                 obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits() &&
208                 obj.getMinimumFractionDigits() == this.getMinimumFractionDigits() &&
209                 obj.isGroupingUsed() == this.isGroupingUsed();
210     }
211 
212     /**
213      * Copies the DecimalFormatSymbols settings into our native peer in bulk.
214      */
setDecimalFormatSymbols(final DecimalFormatSymbols dfs)215     public void setDecimalFormatSymbols(final DecimalFormatSymbols dfs) {
216         setDecimalFormatSymbols(this.address, dfs.getCurrencySymbol(), dfs.getDecimalSeparator(),
217                 dfs.getDigit(), dfs.getExponentSeparator(), dfs.getGroupingSeparator(),
218                 dfs.getInfinity(), dfs.getInternationalCurrencySymbol(), dfs.getMinusSign(),
219                 dfs.getMonetaryDecimalSeparator(), dfs.getNaN(), dfs.getPatternSeparator(),
220                 dfs.getPercent(), dfs.getPerMill(), dfs.getZeroDigit());
221     }
222 
setDecimalFormatSymbols(final LocaleData localeData)223     public void setDecimalFormatSymbols(final LocaleData localeData) {
224         setDecimalFormatSymbols(this.address, localeData.currencySymbol, localeData.decimalSeparator,
225                 '#', localeData.exponentSeparator, localeData.groupingSeparator,
226                 localeData.infinity, localeData.internationalCurrencySymbol, localeData.minusSign,
227                 localeData.monetarySeparator, localeData.NaN, localeData.patternSeparator,
228                 localeData.percent, localeData.perMill, localeData.zeroDigit);
229     }
230 
formatBigDecimal(BigDecimal value, FieldPosition field)231     public char[] formatBigDecimal(BigDecimal value, FieldPosition field) {
232         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
233         char[] result = formatDigitList(this.address, value.toString(), fpi);
234         if (fpi != null) {
235             FieldPositionIterator.setFieldPosition(fpi, field);
236         }
237         return result;
238     }
239 
formatBigInteger(BigInteger value, FieldPosition field)240     public char[] formatBigInteger(BigInteger value, FieldPosition field) {
241         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
242         char[] result = formatDigitList(this.address, value.toString(10), fpi);
243         if (fpi != null) {
244             FieldPositionIterator.setFieldPosition(fpi, field);
245         }
246         return result;
247     }
248 
formatLong(long value, FieldPosition field)249     public char[] formatLong(long value, FieldPosition field) {
250         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
251         char[] result = formatLong(this.address, value, fpi);
252         if (fpi != null) {
253             FieldPositionIterator.setFieldPosition(fpi, field);
254         }
255         return result;
256     }
257 
formatDouble(double value, FieldPosition field)258     public char[] formatDouble(double value, FieldPosition field) {
259         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
260         char[] result = formatDouble(this.address, value, fpi);
261         if (fpi != null) {
262             FieldPositionIterator.setFieldPosition(fpi, field);
263         }
264         return result;
265     }
266 
applyLocalizedPattern(String pattern)267     public void applyLocalizedPattern(String pattern) {
268         applyPattern(this.address, true, pattern);
269         lastPattern = null;
270     }
271 
applyPattern(String pattern)272     public void applyPattern(String pattern) {
273         if (lastPattern != null && pattern.equals(lastPattern)) {
274             return;
275         }
276         applyPattern(this.address, false, pattern);
277         lastPattern = pattern;
278     }
279 
formatToCharacterIterator(Object object)280     public AttributedCharacterIterator formatToCharacterIterator(Object object) {
281         if (!(object instanceof Number)) {
282             throw new IllegalArgumentException();
283         }
284         Number number = (Number) object;
285         FieldPositionIterator fpIter = new FieldPositionIterator();
286         String text;
287         if (number instanceof BigInteger || number instanceof BigDecimal) {
288             text = new String(formatDigitList(this.address, number.toString(), fpIter));
289         } else if (number instanceof Double || number instanceof Float) {
290             double dv = number.doubleValue();
291             text = new String(formatDouble(this.address, dv, fpIter));
292         } else {
293             long lv = number.longValue();
294             text = new String(formatLong(this.address, lv, fpIter));
295         }
296 
297         AttributedString as = new AttributedString(text);
298 
299         while (fpIter.next()) {
300             Format.Field field = fpIter.field();
301             as.addAttribute(field, field, fpIter.start(), fpIter.limit());
302         }
303 
304         // return the CharacterIterator from AttributedString
305         return as.getIterator();
306     }
307 
makeScalePositive(int scale, StringBuilder val)308     private int makeScalePositive(int scale, StringBuilder val) {
309         if (scale < 0) {
310             scale = -scale;
311             for (int i = scale; i > 0; i--) {
312                 val.append('0');
313             }
314             scale = 0;
315         }
316         return scale;
317     }
318 
toLocalizedPattern()319     public String toLocalizedPattern() {
320         return toPatternImpl(this.address, true);
321     }
322 
toPattern()323     public String toPattern() {
324         return toPatternImpl(this.address, false);
325     }
326 
parse(String string, ParsePosition position)327     public Number parse(String string, ParsePosition position) {
328         return parse(address, string, position, parseBigDecimal);
329     }
330 
331     // start getter and setter
332 
getMaximumFractionDigits()333     public int getMaximumFractionDigits() {
334         return getAttribute(this.address, UNUM_MAX_FRACTION_DIGITS);
335     }
336 
getMaximumIntegerDigits()337     public int getMaximumIntegerDigits() {
338         return getAttribute(this.address, UNUM_MAX_INTEGER_DIGITS);
339     }
340 
getMinimumFractionDigits()341     public int getMinimumFractionDigits() {
342         return getAttribute(this.address, UNUM_MIN_FRACTION_DIGITS);
343     }
344 
getMinimumIntegerDigits()345     public int getMinimumIntegerDigits() {
346         return getAttribute(this.address, UNUM_MIN_INTEGER_DIGITS);
347     }
348 
getGroupingSize()349     public int getGroupingSize() {
350         return getAttribute(this.address, UNUM_GROUPING_SIZE);
351     }
352 
getMultiplier()353     public int getMultiplier() {
354         return getAttribute(this.address, UNUM_MULTIPLIER);
355     }
356 
getNegativePrefix()357     public String getNegativePrefix() {
358         if (negPrefNull) {
359             return null;
360         }
361         return getTextAttribute(this.address, UNUM_NEGATIVE_PREFIX);
362     }
363 
getNegativeSuffix()364     public String getNegativeSuffix() {
365         if (negSuffNull) {
366             return null;
367         }
368         return getTextAttribute(this.address, UNUM_NEGATIVE_SUFFIX);
369     }
370 
getPositivePrefix()371     public String getPositivePrefix() {
372         if (posPrefNull) {
373             return null;
374         }
375         return getTextAttribute(this.address, UNUM_POSITIVE_PREFIX);
376     }
377 
getPositiveSuffix()378     public String getPositiveSuffix() {
379         if (posSuffNull) {
380             return null;
381         }
382         return getTextAttribute(this.address, UNUM_POSITIVE_SUFFIX);
383     }
384 
isDecimalSeparatorAlwaysShown()385     public boolean isDecimalSeparatorAlwaysShown() {
386         return getAttribute(this.address, UNUM_DECIMAL_ALWAYS_SHOWN) != 0;
387     }
388 
isParseBigDecimal()389     public boolean isParseBigDecimal() {
390         return parseBigDecimal;
391     }
392 
isParseIntegerOnly()393     public boolean isParseIntegerOnly() {
394         return getAttribute(this.address, UNUM_PARSE_INT_ONLY) != 0;
395     }
396 
isGroupingUsed()397     public boolean isGroupingUsed() {
398         return getAttribute(this.address, UNUM_GROUPING_USED) != 0;
399     }
400 
setDecimalSeparatorAlwaysShown(boolean value)401     public void setDecimalSeparatorAlwaysShown(boolean value) {
402         int i = value ? -1 : 0;
403         setAttribute(this.address, UNUM_DECIMAL_ALWAYS_SHOWN, i);
404     }
405 
setCurrency(Currency currency)406     public void setCurrency(Currency currency) {
407         setSymbol(this.address, UNUM_CURRENCY_SYMBOL, currency.getSymbol());
408         setSymbol(this.address, UNUM_INTL_CURRENCY_SYMBOL, currency.getCurrencyCode());
409     }
410 
setGroupingSize(int value)411     public void setGroupingSize(int value) {
412         setAttribute(this.address, UNUM_GROUPING_SIZE, value);
413     }
414 
setGroupingUsed(boolean value)415     public void setGroupingUsed(boolean value) {
416         int i = value ? -1 : 0;
417         setAttribute(this.address, UNUM_GROUPING_USED, i);
418     }
419 
setMaximumFractionDigits(int value)420     public void setMaximumFractionDigits(int value) {
421         setAttribute(this.address, UNUM_MAX_FRACTION_DIGITS, value);
422     }
423 
setMaximumIntegerDigits(int value)424     public void setMaximumIntegerDigits(int value) {
425         setAttribute(this.address, UNUM_MAX_INTEGER_DIGITS, value);
426     }
427 
setMinimumFractionDigits(int value)428     public void setMinimumFractionDigits(int value) {
429         setAttribute(this.address, UNUM_MIN_FRACTION_DIGITS, value);
430     }
431 
setMinimumIntegerDigits(int value)432     public void setMinimumIntegerDigits(int value) {
433         setAttribute(this.address, UNUM_MIN_INTEGER_DIGITS, value);
434     }
435 
setMultiplier(int value)436     public void setMultiplier(int value) {
437         setAttribute(this.address, UNUM_MULTIPLIER, value);
438         // Update the cached BigDecimal for multiplier.
439         multiplierBigDecimal = BigDecimal.valueOf(value);
440     }
441 
setNegativePrefix(String value)442     public void setNegativePrefix(String value) {
443         negPrefNull = value == null;
444         if (!negPrefNull) {
445             setTextAttribute(this.address, UNUM_NEGATIVE_PREFIX, value);
446         }
447     }
448 
setNegativeSuffix(String value)449     public void setNegativeSuffix(String value) {
450         negSuffNull = value == null;
451         if (!negSuffNull) {
452             setTextAttribute(this.address, UNUM_NEGATIVE_SUFFIX, value);
453         }
454     }
455 
setPositivePrefix(String value)456     public void setPositivePrefix(String value) {
457         posPrefNull = value == null;
458         if (!posPrefNull) {
459             setTextAttribute(this.address, UNUM_POSITIVE_PREFIX, value);
460         }
461     }
462 
setPositiveSuffix(String value)463     public void setPositiveSuffix(String value) {
464         posSuffNull = value == null;
465         if (!posSuffNull) {
466             setTextAttribute(this.address, UNUM_POSITIVE_SUFFIX, value);
467         }
468     }
469 
setParseBigDecimal(boolean value)470     public void setParseBigDecimal(boolean value) {
471         parseBigDecimal = value;
472     }
473 
setParseIntegerOnly(boolean value)474     public void setParseIntegerOnly(boolean value) {
475         int i = value ? -1 : 0;
476         setAttribute(this.address, UNUM_PARSE_INT_ONLY, i);
477     }
478 
applyPattern(int addr, boolean localized, String pattern)479     private static void applyPattern(int addr, boolean localized, String pattern) {
480         try {
481             applyPatternImpl(addr, localized, pattern);
482         } catch (NullPointerException npe) {
483             throw npe;
484         } catch (RuntimeException re) {
485             throw new IllegalArgumentException("syntax error: " + re.getMessage() + ": " + pattern);
486         }
487     }
488 
setRoundingMode(RoundingMode roundingMode, double roundingIncrement)489     public void setRoundingMode(RoundingMode roundingMode, double roundingIncrement) {
490         final int nativeRoundingMode;
491         switch (roundingMode) {
492         case CEILING: nativeRoundingMode = 0; break;
493         case FLOOR: nativeRoundingMode = 1; break;
494         case DOWN: nativeRoundingMode = 2; break;
495         case UP: nativeRoundingMode = 3; break;
496         case HALF_EVEN: nativeRoundingMode = 4; break;
497         case HALF_DOWN: nativeRoundingMode = 5; break;
498         case HALF_UP: nativeRoundingMode = 6; break;
499         default: throw new AssertionError();
500         }
501         setRoundingMode(address, nativeRoundingMode, roundingIncrement);
502     }
503 
504     // Utility to get information about field positions from native (ICU) code.
505     private static class FieldPositionIterator {
506         private int[] data;
507         private int pos = -3; // so first call to next() leaves pos at 0
508 
FieldPositionIterator()509         private FieldPositionIterator() {
510         }
511 
forFieldPosition(FieldPosition fp)512         public static FieldPositionIterator forFieldPosition(FieldPosition fp) {
513             if (fp != null && fp.getField() != -1) {
514                 return new FieldPositionIterator();
515             }
516             return null;
517         }
518 
getNativeFieldPositionId(FieldPosition fp)519         private static int getNativeFieldPositionId(FieldPosition fp) {
520             // NOTE: -1, 0, and 1 were the only valid original java field values
521             // for NumberFormat.  They take precedence.  This assumes any other
522             // value is a mistake and the actual value is in the attribute.
523             // Clients can construct FieldPosition combining any attribute with any field
524             // value, which is just wrong, but there you go.
525 
526             int id = fp.getField();
527             if (id < -1 || id > 1) {
528                 id = -1;
529             }
530             if (id == -1) {
531                 Format.Field attr = fp.getFieldAttribute();
532                 if (attr != null) {
533                     for (int i = 0; i < fields.length; ++i) {
534                         if (fields[i].equals(attr)) {
535                             id = i;
536                             break;
537                         }
538                     }
539                 }
540             }
541             return id;
542         }
543 
setFieldPosition(FieldPositionIterator fpi, FieldPosition fp)544         private static void setFieldPosition(FieldPositionIterator fpi, FieldPosition fp) {
545             if (fpi != null && fp != null) {
546                 int field = getNativeFieldPositionId(fp);
547                 if (field != -1) {
548                     while (fpi.next()) {
549                         if (fpi.fieldId() == field) {
550                             fp.setBeginIndex(fpi.start());
551                             fp.setEndIndex(fpi.limit());
552                             break;
553                         }
554                     }
555                 }
556             }
557         }
558 
next()559         public boolean next() {
560             // if pos == data.length, we've already returned false once
561             if (data == null || pos == data.length) {
562                 throw new NoSuchElementException();
563             }
564             pos += 3;
565             return pos < data.length;
566         }
567 
checkValid()568         private void checkValid() {
569             if (data == null || pos < 0 || pos == data.length) {
570                 throw new NoSuchElementException();
571             }
572         }
573 
fieldId()574         public int fieldId() {
575             return data[pos];
576         }
577 
field()578         public Format.Field field() {
579             checkValid();
580             return fields[data[pos]];
581         }
582 
start()583         public int start() {
584             checkValid();
585             return data[pos + 1];
586         }
587 
limit()588         public int limit() {
589             checkValid();
590             return data[pos + 2];
591         }
592 
593         private static Format.Field fields[] = {
594             // The old java field values were 0 for integer and 1 for fraction.
595             // The new java field attributes are all objects.  ICU assigns the values
596             // starting from 0 in the following order; note that integer and
597             // fraction positions match the old field values.
598             NumberFormat.Field.INTEGER,
599             NumberFormat.Field.FRACTION,
600             NumberFormat.Field.DECIMAL_SEPARATOR,
601             NumberFormat.Field.EXPONENT_SYMBOL,
602             NumberFormat.Field.EXPONENT_SIGN,
603             NumberFormat.Field.EXPONENT,
604             NumberFormat.Field.GROUPING_SEPARATOR,
605             NumberFormat.Field.CURRENCY,
606             NumberFormat.Field.PERCENT,
607             NumberFormat.Field.PERMILLE,
608             NumberFormat.Field.SIGN,
609         };
610 
611         // called by native
setData(int[] data)612         private void setData(int[] data) {
613             this.data = data;
614             this.pos = -3;
615         }
616     }
617 
applyPatternImpl(int addr, boolean localized, String pattern)618     private static native void applyPatternImpl(int addr, boolean localized, String pattern);
cloneImpl(int addr)619     private static native int cloneImpl(int addr);
close(int addr)620     private static native void close(int addr);
formatLong(int addr, long value, FieldPositionIterator iter)621     private static native char[] formatLong(int addr, long value, FieldPositionIterator iter);
formatDouble(int addr, double value, FieldPositionIterator iter)622     private static native char[] formatDouble(int addr, double value, FieldPositionIterator iter);
formatDigitList(int addr, String value, FieldPositionIterator iter)623     private static native char[] formatDigitList(int addr, String value, FieldPositionIterator iter);
getAttribute(int addr, int symbol)624     private static native int getAttribute(int addr, int symbol);
getTextAttribute(int addr, int symbol)625     private static native String getTextAttribute(int addr, int symbol);
open(String pattern, String currencySymbol, char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator, String infinity, String internationalCurrencySymbol, char minusSign, char monetaryDecimalSeparator, String nan, char patternSeparator, char percent, char perMill, char zeroDigit)626     private static native int open(String pattern, String currencySymbol,
627             char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
628             String infinity, String internationalCurrencySymbol, char minusSign,
629             char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
630             char perMill, char zeroDigit);
parse(int addr, String string, ParsePosition position, boolean parseBigDecimal)631     private static native Number parse(int addr, String string, ParsePosition position, boolean parseBigDecimal);
setDecimalFormatSymbols(int addr, String currencySymbol, char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator, String infinity, String internationalCurrencySymbol, char minusSign, char monetaryDecimalSeparator, String nan, char patternSeparator, char percent, char perMill, char zeroDigit)632     private static native void setDecimalFormatSymbols(int addr, String currencySymbol,
633             char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
634             String infinity, String internationalCurrencySymbol, char minusSign,
635             char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
636             char perMill, char zeroDigit);
setSymbol(int addr, int symbol, String str)637     private static native void setSymbol(int addr, int symbol, String str);
setAttribute(int addr, int symbol, int i)638     private static native void setAttribute(int addr, int symbol, int i);
setRoundingMode(int addr, int roundingMode, double roundingIncrement)639     private static native void setRoundingMode(int addr, int roundingMode, double roundingIncrement);
setTextAttribute(int addr, int symbol, String str)640     private static native void setTextAttribute(int addr, int symbol, String str);
toPatternImpl(int addr, boolean localized)641     private static native String toPatternImpl(int addr, boolean localized);
642 }
643