Home | History | Annotate | Download | only in icu
      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 implements Cloneable {
     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 long 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 
    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.
    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     public synchronized void close() {
    148         if (address != 0) {
    149             close(address);
    150             address = 0;
    151         }
    152     }
    153 
    154     @Override protected void finalize() throws Throwable {
    155         try {
    156             close();
    157         } finally {
    158             super.finalize();
    159         }
    160     }
    161 
    162     @Override public Object clone() {
    163         try {
    164             NativeDecimalFormat clone = (NativeDecimalFormat) super.clone();
    165             clone.address = cloneImpl(address);
    166             clone.lastPattern = lastPattern;
    167             clone.negPrefNull = negPrefNull;
    168             clone.negSuffNull = negSuffNull;
    169             clone.posPrefNull = posPrefNull;
    170             clone.posSuffNull = posSuffNull;
    171             return clone;
    172         } catch (CloneNotSupportedException unexpected) {
    173             throw new AssertionError(unexpected);
    174         }
    175     }
    176 
    177     /**
    178      * Note: this doesn't check that the underlying native DecimalFormat objects' configured
    179      * native DecimalFormatSymbols objects are equal. It is assumed that the
    180      * caller (DecimalFormat) will check the DecimalFormatSymbols objects
    181      * instead, for performance.
    182      *
    183      * This is also unreasonably expensive, calling down to JNI multiple times.
    184      *
    185      * TODO: remove this and just have DecimalFormat.equals do the right thing itself.
    186      */
    187     @Override
    188     public boolean equals(Object object) {
    189         if (object == this) {
    190             return true;
    191         }
    192         if (!(object instanceof NativeDecimalFormat)) {
    193             return false;
    194         }
    195         NativeDecimalFormat obj = (NativeDecimalFormat) object;
    196         if (obj.address == this.address) {
    197             return true;
    198         }
    199         return obj.toPattern().equals(this.toPattern()) &&
    200                 obj.isDecimalSeparatorAlwaysShown() == this.isDecimalSeparatorAlwaysShown() &&
    201                 obj.getGroupingSize() == this.getGroupingSize() &&
    202                 obj.getMultiplier() == this.getMultiplier() &&
    203                 obj.getNegativePrefix().equals(this.getNegativePrefix()) &&
    204                 obj.getNegativeSuffix().equals(this.getNegativeSuffix()) &&
    205                 obj.getPositivePrefix().equals(this.getPositivePrefix()) &&
    206                 obj.getPositiveSuffix().equals(this.getPositiveSuffix()) &&
    207                 obj.getMaximumIntegerDigits() == this.getMaximumIntegerDigits() &&
    208                 obj.getMaximumFractionDigits() == this.getMaximumFractionDigits() &&
    209                 obj.getMinimumIntegerDigits() == this.getMinimumIntegerDigits() &&
    210                 obj.getMinimumFractionDigits() == this.getMinimumFractionDigits() &&
    211                 obj.isGroupingUsed() == this.isGroupingUsed();
    212     }
    213 
    214     /**
    215      * Copies the DecimalFormatSymbols settings into our native peer in bulk.
    216      */
    217     public void setDecimalFormatSymbols(final DecimalFormatSymbols dfs) {
    218         setDecimalFormatSymbols(this.address, dfs.getCurrencySymbol(), dfs.getDecimalSeparator(),
    219                 dfs.getDigit(), dfs.getExponentSeparator(), dfs.getGroupingSeparator(),
    220                 dfs.getInfinity(), dfs.getInternationalCurrencySymbol(), dfs.getMinusSign(),
    221                 dfs.getMonetaryDecimalSeparator(), dfs.getNaN(), dfs.getPatternSeparator(),
    222                 dfs.getPercent(), dfs.getPerMill(), dfs.getZeroDigit());
    223     }
    224 
    225     public void setDecimalFormatSymbols(final LocaleData localeData) {
    226         setDecimalFormatSymbols(this.address, localeData.currencySymbol, localeData.decimalSeparator,
    227                 '#', localeData.exponentSeparator, localeData.groupingSeparator,
    228                 localeData.infinity, localeData.internationalCurrencySymbol, localeData.minusSign,
    229                 localeData.monetarySeparator, localeData.NaN, localeData.patternSeparator,
    230                 localeData.percent, localeData.perMill, localeData.zeroDigit);
    231     }
    232 
    233     public char[] formatBigDecimal(BigDecimal value, FieldPosition field) {
    234         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
    235         char[] result = formatDigitList(this.address, value.toString(), fpi);
    236         if (fpi != null) {
    237             FieldPositionIterator.setFieldPosition(fpi, field);
    238         }
    239         return result;
    240     }
    241 
    242     public char[] formatBigInteger(BigInteger value, FieldPosition field) {
    243         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
    244         char[] result = formatDigitList(this.address, value.toString(10), fpi);
    245         if (fpi != null) {
    246             FieldPositionIterator.setFieldPosition(fpi, field);
    247         }
    248         return result;
    249     }
    250 
    251     public char[] formatLong(long value, FieldPosition field) {
    252         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
    253         char[] result = formatLong(this.address, value, fpi);
    254         if (fpi != null) {
    255             FieldPositionIterator.setFieldPosition(fpi, field);
    256         }
    257         return result;
    258     }
    259 
    260     public char[] formatDouble(double value, FieldPosition field) {
    261         FieldPositionIterator fpi = FieldPositionIterator.forFieldPosition(field);
    262         char[] result = formatDouble(this.address, value, fpi);
    263         if (fpi != null) {
    264             FieldPositionIterator.setFieldPosition(fpi, field);
    265         }
    266         return result;
    267     }
    268 
    269     public void applyLocalizedPattern(String pattern) {
    270         applyPattern(this.address, true, pattern);
    271         lastPattern = null;
    272     }
    273 
    274     public void applyPattern(String pattern) {
    275         if (lastPattern != null && pattern.equals(lastPattern)) {
    276             return;
    277         }
    278         applyPattern(this.address, false, pattern);
    279         lastPattern = pattern;
    280     }
    281 
    282     public AttributedCharacterIterator formatToCharacterIterator(Object object) {
    283         if (object == null) {
    284             throw new NullPointerException("object == null");
    285         }
    286         if (!(object instanceof Number)) {
    287             throw new IllegalArgumentException("object not a Number: " + object.getClass());
    288         }
    289         Number number = (Number) object;
    290         FieldPositionIterator fpIter = new FieldPositionIterator();
    291         String text;
    292         if (number instanceof BigInteger || number instanceof BigDecimal) {
    293             text = new String(formatDigitList(this.address, number.toString(), fpIter));
    294         } else if (number instanceof Double || number instanceof Float) {
    295             double dv = number.doubleValue();
    296             text = new String(formatDouble(this.address, dv, fpIter));
    297         } else {
    298             long lv = number.longValue();
    299             text = new String(formatLong(this.address, lv, fpIter));
    300         }
    301 
    302         AttributedString as = new AttributedString(text);
    303 
    304         while (fpIter.next()) {
    305             Format.Field field = fpIter.field();
    306             as.addAttribute(field, field, fpIter.start(), fpIter.limit());
    307         }
    308 
    309         // return the CharacterIterator from AttributedString
    310         return as.getIterator();
    311     }
    312 
    313     private int makeScalePositive(int scale, StringBuilder val) {
    314         if (scale < 0) {
    315             scale = -scale;
    316             for (int i = scale; i > 0; i--) {
    317                 val.append('0');
    318             }
    319             scale = 0;
    320         }
    321         return scale;
    322     }
    323 
    324     public String toLocalizedPattern() {
    325         return toPatternImpl(this.address, true);
    326     }
    327 
    328     public String toPattern() {
    329         return toPatternImpl(this.address, false);
    330     }
    331 
    332     public Number parse(String string, ParsePosition position) {
    333         return parse(address, string, position, parseBigDecimal);
    334     }
    335 
    336     // start getter and setter
    337 
    338     public int getMaximumFractionDigits() {
    339         return getAttribute(this.address, UNUM_MAX_FRACTION_DIGITS);
    340     }
    341 
    342     public int getMaximumIntegerDigits() {
    343         return getAttribute(this.address, UNUM_MAX_INTEGER_DIGITS);
    344     }
    345 
    346     public int getMinimumFractionDigits() {
    347         return getAttribute(this.address, UNUM_MIN_FRACTION_DIGITS);
    348     }
    349 
    350     public int getMinimumIntegerDigits() {
    351         return getAttribute(this.address, UNUM_MIN_INTEGER_DIGITS);
    352     }
    353 
    354     public int getGroupingSize() {
    355         return getAttribute(this.address, UNUM_GROUPING_SIZE);
    356     }
    357 
    358     public int getMultiplier() {
    359         return getAttribute(this.address, UNUM_MULTIPLIER);
    360     }
    361 
    362     public String getNegativePrefix() {
    363         if (negPrefNull) {
    364             return null;
    365         }
    366         return getTextAttribute(this.address, UNUM_NEGATIVE_PREFIX);
    367     }
    368 
    369     public String getNegativeSuffix() {
    370         if (negSuffNull) {
    371             return null;
    372         }
    373         return getTextAttribute(this.address, UNUM_NEGATIVE_SUFFIX);
    374     }
    375 
    376     public String getPositivePrefix() {
    377         if (posPrefNull) {
    378             return null;
    379         }
    380         return getTextAttribute(this.address, UNUM_POSITIVE_PREFIX);
    381     }
    382 
    383     public String getPositiveSuffix() {
    384         if (posSuffNull) {
    385             return null;
    386         }
    387         return getTextAttribute(this.address, UNUM_POSITIVE_SUFFIX);
    388     }
    389 
    390     public boolean isDecimalSeparatorAlwaysShown() {
    391         return getAttribute(this.address, UNUM_DECIMAL_ALWAYS_SHOWN) != 0;
    392     }
    393 
    394     public boolean isParseBigDecimal() {
    395         return parseBigDecimal;
    396     }
    397 
    398     public boolean isParseIntegerOnly() {
    399         return getAttribute(this.address, UNUM_PARSE_INT_ONLY) != 0;
    400     }
    401 
    402     public boolean isGroupingUsed() {
    403         return getAttribute(this.address, UNUM_GROUPING_USED) != 0;
    404     }
    405 
    406     public void setDecimalSeparatorAlwaysShown(boolean value) {
    407         int i = value ? -1 : 0;
    408         setAttribute(this.address, UNUM_DECIMAL_ALWAYS_SHOWN, i);
    409     }
    410 
    411     public void setCurrency(Currency currency) {
    412         setSymbol(this.address, UNUM_CURRENCY_SYMBOL, currency.getSymbol());
    413         setSymbol(this.address, UNUM_INTL_CURRENCY_SYMBOL, currency.getCurrencyCode());
    414     }
    415 
    416     public void setGroupingSize(int value) {
    417         setAttribute(this.address, UNUM_GROUPING_SIZE, value);
    418     }
    419 
    420     public void setGroupingUsed(boolean value) {
    421         int i = value ? -1 : 0;
    422         setAttribute(this.address, UNUM_GROUPING_USED, i);
    423     }
    424 
    425     public void setMaximumFractionDigits(int value) {
    426         setAttribute(this.address, UNUM_MAX_FRACTION_DIGITS, value);
    427     }
    428 
    429     public void setMaximumIntegerDigits(int value) {
    430         setAttribute(this.address, UNUM_MAX_INTEGER_DIGITS, value);
    431     }
    432 
    433     public void setMinimumFractionDigits(int value) {
    434         setAttribute(this.address, UNUM_MIN_FRACTION_DIGITS, value);
    435     }
    436 
    437     public void setMinimumIntegerDigits(int value) {
    438         setAttribute(this.address, UNUM_MIN_INTEGER_DIGITS, value);
    439     }
    440 
    441     public void setMultiplier(int value) {
    442         setAttribute(this.address, UNUM_MULTIPLIER, value);
    443         // Update the cached BigDecimal for multiplier.
    444         multiplierBigDecimal = BigDecimal.valueOf(value);
    445     }
    446 
    447     public void setNegativePrefix(String value) {
    448         negPrefNull = value == null;
    449         if (!negPrefNull) {
    450             setTextAttribute(this.address, UNUM_NEGATIVE_PREFIX, value);
    451         }
    452     }
    453 
    454     public void setNegativeSuffix(String value) {
    455         negSuffNull = value == null;
    456         if (!negSuffNull) {
    457             setTextAttribute(this.address, UNUM_NEGATIVE_SUFFIX, value);
    458         }
    459     }
    460 
    461     public void setPositivePrefix(String value) {
    462         posPrefNull = value == null;
    463         if (!posPrefNull) {
    464             setTextAttribute(this.address, UNUM_POSITIVE_PREFIX, value);
    465         }
    466     }
    467 
    468     public void setPositiveSuffix(String value) {
    469         posSuffNull = value == null;
    470         if (!posSuffNull) {
    471             setTextAttribute(this.address, UNUM_POSITIVE_SUFFIX, value);
    472         }
    473     }
    474 
    475     public void setParseBigDecimal(boolean value) {
    476         parseBigDecimal = value;
    477     }
    478 
    479     public void setParseIntegerOnly(boolean value) {
    480         int i = value ? -1 : 0;
    481         setAttribute(this.address, UNUM_PARSE_INT_ONLY, i);
    482     }
    483 
    484     private static void applyPattern(long addr, boolean localized, String pattern) {
    485         try {
    486             applyPatternImpl(addr, localized, pattern);
    487         } catch (NullPointerException npe) {
    488             throw npe;
    489         } catch (RuntimeException re) {
    490             throw new IllegalArgumentException("syntax error: " + re.getMessage() + ": " + pattern);
    491         }
    492     }
    493 
    494     public void setRoundingMode(RoundingMode roundingMode, double roundingIncrement) {
    495         final int nativeRoundingMode;
    496         switch (roundingMode) {
    497         case CEILING: nativeRoundingMode = 0; break;
    498         case FLOOR: nativeRoundingMode = 1; break;
    499         case DOWN: nativeRoundingMode = 2; break;
    500         case UP: nativeRoundingMode = 3; break;
    501         case HALF_EVEN: nativeRoundingMode = 4; break;
    502         case HALF_DOWN: nativeRoundingMode = 5; break;
    503         case HALF_UP: nativeRoundingMode = 6; break;
    504         default: throw new AssertionError();
    505         }
    506         setRoundingMode(address, nativeRoundingMode, roundingIncrement);
    507     }
    508 
    509     // Utility to get information about field positions from native (ICU) code.
    510     private static class FieldPositionIterator {
    511         private int[] data;
    512         private int pos = -3; // so first call to next() leaves pos at 0
    513 
    514         private FieldPositionIterator() {
    515         }
    516 
    517         public static FieldPositionIterator forFieldPosition(FieldPosition fp) {
    518             if (fp != null && fp.getField() != -1) {
    519                 return new FieldPositionIterator();
    520             }
    521             return null;
    522         }
    523 
    524         private static int getNativeFieldPositionId(FieldPosition fp) {
    525             // NOTE: -1, 0, and 1 were the only valid original java field values
    526             // for NumberFormat.  They take precedence.  This assumes any other
    527             // value is a mistake and the actual value is in the attribute.
    528             // Clients can construct FieldPosition combining any attribute with any field
    529             // value, which is just wrong, but there you go.
    530 
    531             int id = fp.getField();
    532             if (id < -1 || id > 1) {
    533                 id = -1;
    534             }
    535             if (id == -1) {
    536                 Format.Field attr = fp.getFieldAttribute();
    537                 if (attr != null) {
    538                     for (int i = 0; i < fields.length; ++i) {
    539                         if (fields[i].equals(attr)) {
    540                             id = i;
    541                             break;
    542                         }
    543                     }
    544                 }
    545             }
    546             return id;
    547         }
    548 
    549         private static void setFieldPosition(FieldPositionIterator fpi, FieldPosition fp) {
    550             if (fpi != null && fp != null) {
    551                 int field = getNativeFieldPositionId(fp);
    552                 if (field != -1) {
    553                     while (fpi.next()) {
    554                         if (fpi.fieldId() == field) {
    555                             fp.setBeginIndex(fpi.start());
    556                             fp.setEndIndex(fpi.limit());
    557                             break;
    558                         }
    559                     }
    560                 }
    561             }
    562         }
    563 
    564         public boolean next() {
    565             // if pos == data.length, we've already returned false once
    566             if (data == null || pos == data.length) {
    567                 throw new NoSuchElementException();
    568             }
    569             pos += 3;
    570             return pos < data.length;
    571         }
    572 
    573         private void checkValid() {
    574             if (data == null || pos < 0 || pos == data.length) {
    575                 throw new NoSuchElementException();
    576             }
    577         }
    578 
    579         public int fieldId() {
    580             return data[pos];
    581         }
    582 
    583         public Format.Field field() {
    584             checkValid();
    585             return fields[data[pos]];
    586         }
    587 
    588         public int start() {
    589             checkValid();
    590             return data[pos + 1];
    591         }
    592 
    593         public int limit() {
    594             checkValid();
    595             return data[pos + 2];
    596         }
    597 
    598         private static Format.Field fields[] = {
    599             // The old java field values were 0 for integer and 1 for fraction.
    600             // The new java field attributes are all objects.  ICU assigns the values
    601             // starting from 0 in the following order; note that integer and
    602             // fraction positions match the old field values.
    603             NumberFormat.Field.INTEGER,
    604             NumberFormat.Field.FRACTION,
    605             NumberFormat.Field.DECIMAL_SEPARATOR,
    606             NumberFormat.Field.EXPONENT_SYMBOL,
    607             NumberFormat.Field.EXPONENT_SIGN,
    608             NumberFormat.Field.EXPONENT,
    609             NumberFormat.Field.GROUPING_SEPARATOR,
    610             NumberFormat.Field.CURRENCY,
    611             NumberFormat.Field.PERCENT,
    612             NumberFormat.Field.PERMILLE,
    613             NumberFormat.Field.SIGN,
    614         };
    615 
    616         // called by native
    617         private void setData(int[] data) {
    618             this.data = data;
    619             this.pos = -3;
    620         }
    621     }
    622 
    623     private static native void applyPatternImpl(long addr, boolean localized, String pattern);
    624     private static native long cloneImpl(long addr);
    625     private static native void close(long addr);
    626     private static native char[] formatLong(long addr, long value, FieldPositionIterator iter);
    627     private static native char[] formatDouble(long addr, double value, FieldPositionIterator iter);
    628     private static native char[] formatDigitList(long addr, String value, FieldPositionIterator iter);
    629     private static native int getAttribute(long addr, int symbol);
    630     private static native String getTextAttribute(long addr, int symbol);
    631     private static native long open(String pattern, String currencySymbol,
    632             char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
    633             String infinity, String internationalCurrencySymbol, char minusSign,
    634             char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
    635             char perMill, char zeroDigit);
    636     private static native Number parse(long addr, String string, ParsePosition position, boolean parseBigDecimal);
    637     private static native void setDecimalFormatSymbols(long addr, String currencySymbol,
    638             char decimalSeparator, char digit, String exponentSeparator, char groupingSeparator,
    639             String infinity, String internationalCurrencySymbol, char minusSign,
    640             char monetaryDecimalSeparator, String nan, char patternSeparator, char percent,
    641             char perMill, char zeroDigit);
    642     private static native void setSymbol(long addr, int symbol, String str);
    643     private static native void setAttribute(long addr, int symbol, int i);
    644     private static native void setRoundingMode(long addr, int roundingMode, double roundingIncrement);
    645     private static native void setTextAttribute(long addr, int symbol, String str);
    646     private static native String toPatternImpl(long addr, boolean localized);
    647 }
    648