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