Index: main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java =================================================================== --- main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java (revision 40289) +++ main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java (working copy) @@ -3,7 +3,7 @@ package com.ibm.icu.impl.number; import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat.Field; +import com.ibm.icu.text.NumberFormat; /** * Performs manipulations on affix patterns: the prefix and suffix strings associated with a decimal @@ -35,21 +35,7 @@ * case AffixPatternUtils.TYPE_PERCENT: * // Current token is a percent sign. * break; - * case AffixPatternUtils.TYPE_PERMILLE: - * // Current token is a permille sign. - * break; - * case AffixPatternUtils.TYPE_CURRENCY_SINGLE: - * // Current token is a single currency sign. - * break; - * case AffixPatternUtils.TYPE_CURRENCY_DOUBLE: - * // Current token is a double currency sign. - * break; - * case AffixPatternUtils.TYPE_CURRENCY_TRIPLE: - * // Current token is a triple currency sign. - * break; - * case AffixPatternUtils.TYPE_CURRENCY_OVERFLOW: - * // Current token has four or more currency signs. - * break; + * // ... other types ... * default: * // Current token is an arbitrary code point. * // The variable typeOrCp is the code point. @@ -67,8 +53,11 @@ private static final int STATE_FIRST_CURR = 4; private static final int STATE_SECOND_CURR = 5; private static final int STATE_THIRD_CURR = 6; - private static final int STATE_OVERFLOW_CURR = 7; + private static final int STATE_FOURTH_CURR = 7; + private static final int STATE_FIFTH_CURR = 8; + private static final int STATE_OVERFLOW_CURR = 9; + /** Represents a literal character; the value is stored in the code point field. */ private static final int TYPE_CODEPOINT = 0; /** Represents a minus sign symbol '-'. */ @@ -92,7 +81,13 @@ /** Represents a triple currency symbol '¤¤¤'. */ public static final int TYPE_CURRENCY_TRIPLE = -7; - /** Represents a sequence of four or more currency symbols. */ + /** Represents a quadruple currency symbol '¤¤¤¤'. */ + public static final int TYPE_CURRENCY_QUAD = -8; + + /** Represents a quintuple currency symbol '¤¤¤¤¤'. */ + public static final int TYPE_CURRENCY_QUINT = -9; + + /** Represents a sequence of six or more currency symbols. */ public static final int TYPE_CURRENCY_OVERFLOW = -15; /** @@ -227,6 +222,13 @@ return output.length() - startLength; } + /** Version of {@link #escape} that returns a String. */ + public static String escape(CharSequence input) { + StringBuilder sb = new StringBuilder(); + escape(input, sb); + return sb.toString(); + } + /** * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", and "‰" * with their localized equivalents. Replaces "¤", "¤¤", and "¤¤¤" with the three argument @@ -256,30 +258,38 @@ while (hasNext(tag, affixPattern)) { tag = nextToken(tag, affixPattern); int typeOrCp = getTypeOrCp(tag); + NumberFormat.Field field = (typeOrCp < 0) ? getFieldForType(typeOrCp) : null; switch (typeOrCp) { case TYPE_MINUS_SIGN: - output.append(minusSign, Field.SIGN); + output.append(minusSign, field); break; case TYPE_PLUS_SIGN: - output.append(symbols.getPlusSignString(), Field.SIGN); + output.append(symbols.getPlusSignString(), field); break; case TYPE_PERCENT: - output.append(symbols.getPercentString(), Field.PERCENT); + output.append(symbols.getPercentString(), field); break; case TYPE_PERMILLE: - output.append(symbols.getPerMillString(), Field.PERMILLE); + output.append(symbols.getPerMillString(), field); break; case TYPE_CURRENCY_SINGLE: - output.append(currency1, Field.CURRENCY); + output.append(currency1, field); break; case TYPE_CURRENCY_DOUBLE: - output.append(currency2, Field.CURRENCY); + output.append(currency2, field); break; case TYPE_CURRENCY_TRIPLE: - output.append(currency3, Field.CURRENCY); + output.append(currency3, field); break; + case TYPE_CURRENCY_QUAD: + output.appendCodePoint('\uFFFD', field); + break; + case TYPE_CURRENCY_QUINT: + // TODO: Add support for narrow currency symbols here. + output.appendCodePoint('\uFFFD', field); + break; case TYPE_CURRENCY_OVERFLOW: - output.append("\uFFFD", Field.CURRENCY); + output.appendCodePoint('\uFFFD', field); break; default: output.appendCodePoint(typeOrCp, null); @@ -288,6 +298,61 @@ } } + public static final NumberFormat.Field getFieldForType(int type) { + switch (type) { + case TYPE_MINUS_SIGN: + return NumberFormat.Field.SIGN; + case TYPE_PLUS_SIGN: + return NumberFormat.Field.SIGN; + case TYPE_PERCENT: + return NumberFormat.Field.PERCENT; + case TYPE_PERMILLE: + return NumberFormat.Field.PERMILLE; + case TYPE_CURRENCY_SINGLE: + return NumberFormat.Field.CURRENCY; + case TYPE_CURRENCY_DOUBLE: + return NumberFormat.Field.CURRENCY; + case TYPE_CURRENCY_TRIPLE: + return NumberFormat.Field.CURRENCY; + case TYPE_CURRENCY_QUAD: + return NumberFormat.Field.CURRENCY; + case TYPE_CURRENCY_QUINT: + return NumberFormat.Field.CURRENCY; + case TYPE_CURRENCY_OVERFLOW: + return NumberFormat.Field.CURRENCY; + default: + throw new AssertionError(); + } + } + + public static interface SymbolProvider { + public CharSequence getSymbol(int type); + } + + public static int unescape( + CharSequence affixPattern, + NumberStringBuilder output, + int position, + SymbolProvider provider) { + // TODO: Is it worth removing this extra local object instantiation here? + NumberStringBuilder local = new NumberStringBuilder(10); + assert affixPattern != null; + long tag = 0L; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern); + int typeOrCp = getTypeOrCp(tag); + if (typeOrCp == TYPE_CURRENCY_OVERFLOW) { + // Don't go to the provider for this special case + local.appendCodePoint(0xFFFD, NumberFormat.Field.CURRENCY); + } else if (typeOrCp < 0) { + local.append(provider.getSymbol(typeOrCp), getFieldForType(typeOrCp)); + } else { + local.appendCodePoint(typeOrCp, null); + } + } + return output.insert(position, local); + } + /** * Checks whether the given affix pattern contains at least one token of the given type, which is * one of the constants "TYPE_" in {@link AffixPatternUtils}. @@ -320,10 +385,7 @@ while (hasNext(tag, affixPattern)) { tag = nextToken(tag, affixPattern); int typeOrCp = getTypeOrCp(tag); - if (typeOrCp == AffixPatternUtils.TYPE_CURRENCY_SINGLE - || typeOrCp == AffixPatternUtils.TYPE_CURRENCY_DOUBLE - || typeOrCp == AffixPatternUtils.TYPE_CURRENCY_TRIPLE - || typeOrCp == AffixPatternUtils.TYPE_CURRENCY_OVERFLOW) { + if (typeOrCp < 0 && getFieldForType(typeOrCp) == NumberFormat.Field.CURRENCY) { return true; } } @@ -437,7 +499,7 @@ } case STATE_THIRD_CURR: if (cp == '¤') { - state = STATE_OVERFLOW_CURR; + state = STATE_FOURTH_CURR; offset += count; // continue to the next code point break; @@ -444,6 +506,24 @@ } else { return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); } + case STATE_FOURTH_CURR: + if (cp == '¤') { + state = STATE_FIFTH_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); + } + case STATE_FIFTH_CURR: + if (cp == '¤') { + state = STATE_OVERFLOW_CURR; + offset += count; + // continue to the next code point + break; + } else { + return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); + } case STATE_OVERFLOW_CURR: if (cp == '¤') { offset += count; @@ -475,6 +555,10 @@ return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); case STATE_THIRD_CURR: return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); + case STATE_FOURTH_CURR: + return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); + case STATE_FIFTH_CURR: + return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); case STATE_OVERFLOW_CURR: return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); default: @@ -522,6 +606,17 @@ return (type == 0) ? getCodePoint(tag) : -type; } + /** + * Encodes the given values into a 64-bit tag. + * + *