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