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