From: Shane Carr Date: Sat, 26 Aug 2017 07:22:38 +0000 (+0000) Subject: ICU-13177 Simplifying the Modifier interface by removing getPrefix() and getSuffix... X-Git-Tag: release-60-rc~98^2~28 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=20e5b7b9101a7f6a2e245994ab2bf5d1e473c34f;p=icu ICU-13177 Simplifying the Modifier interface by removing getPrefix() and getSuffix() and replacing it with getPrefixLength(), which exposes the same information. X-SVN-Rev: 40358 --- diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java index c49ad5ecdf5..56c824c08ce 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java @@ -98,7 +98,7 @@ public class AffixPatternUtils { * @param patternString The original string whose width will be estimated. * @return The length of the unescaped string. */ - public static int unescapedLength(CharSequence patternString) { + public static int estimateLength(CharSequence patternString) { if (patternString == null) return 0; int state = STATE_BASE; int offset = 0; @@ -292,7 +292,10 @@ public class AffixPatternUtils { local.appendCodePoint(typeOrCp, null); } } - return output.insert(position, local); + if (output != null) { + output.insert(position, local); + } + return local.length(); } /** diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java index 10b5e05ca00..8bb55e145dd 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java @@ -9,10 +9,9 @@ import com.ibm.icu.impl.number.modifiers.PositiveNegativeAffixModifier; import com.ibm.icu.impl.number.modifiers.SimpleModifier; /** - * A Modifier is an immutable object that can be passed through the formatting pipeline until it is - * finally applied to the string builder. A Modifier usually contains a prefix and a suffix that are - * applied, but it could contain something else, like a {@link com.ibm.icu.text.SimpleFormatter} - * pattern. + * A Modifier is an immutable object that can be passed through the formatting pipeline until it is finally applied to + * the string builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain + * something else, like a {@link com.ibm.icu.text.SimpleFormatter} pattern. * * @see PositiveNegativeAffixModifier * @see ConstantAffixModifier @@ -21,101 +20,92 @@ import com.ibm.icu.impl.number.modifiers.SimpleModifier; */ public interface Modifier { - /** - * Apply this Modifier to the string builder. - * - * @param output The string builder to which to apply this modifier. - * @param leftIndex The left index of the string within the builder. Equal to 0 when only one - * number is being formatted. - * @param rightIndex The right index of the string within the string builder. Equal to length-1 - * when only one number is being formatted. - * @return The number of characters (UTF-16 code units) that were added to the string builder. - */ - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex); - - /** - * Whether this modifier is strong. If a modifier is strong, it should always be applied - * immediately and not allowed to bubble up. With regard to padding, strong modifiers are - * considered to be on the inside of the prefix and suffix. - * - * @return Whether the modifier is strong. - */ - public boolean isStrong(); - - /** - * Gets the prefix string associated with this modifier, defined as the string that will be - * inserted at leftIndex when {@link #apply} is called. - * - *

TODO: Change this to appendPrefixTo(), or remove it entirely and do something different at - * the call sites. - * - * @return The prefix string. Will not be null. - */ - public String getPrefix(); - - /** - * Gets the prefix string associated with this modifier, defined as the string that will be - * inserted at rightIndex when {@link #apply} is called. - * - *

TODO: Change this to appendPrefixTo(), or remove it entirely and do something different at - * the call sites. - * - * @return The suffix string. Will not be null. - */ - public String getSuffix(); + /** + * Apply this Modifier to the string builder. + * + * @param output + * The string builder to which to apply this modifier. + * @param leftIndex + * The left index of the string within the builder. Equal to 0 when only one number is being formatted. + * @param rightIndex + * The right index of the string within the string builder. Equal to length-1 when only one number is + * being formatted. + * @return The number of characters (UTF-16 code units) that were added to the string builder. + */ + public int apply(NumberStringBuilder output, int leftIndex, int rightIndex); - /** - * An interface for a modifier that contains both a positive and a negative form. Note that a - * class implementing {@link PositiveNegativeModifier} is not necessarily a {@link Modifier} - * itself. Rather, it returns a {@link Modifier} when {@link #getModifier} is called. - */ - public static interface PositiveNegativeModifier { /** - * Converts this {@link PositiveNegativeModifier} to a {@link Modifier} given the negative sign. + * Gets the length of the prefix. This information can be used in combination with {@link #apply} to extract the + * prefix and suffix strings. * - * @param isNegative true if the negative form of this modifier should be used; false if the - * positive form should be used. - * @return A Modifier corresponding to the negative sign. + * @return The number of characters (UTF-16 code units) in the prefix. */ - public Modifier getModifier(boolean isNegative); - } + public int getPrefixLength(); - /** - * An interface for a modifier that contains both a positive and a negative form for all six - * standard plurals. Note that a class implementing {@link PositiveNegativePluralModifier} is not - * necessarily a {@link Modifier} itself. Rather, it returns a {@link Modifier} when {@link - * #getModifier} is called. - */ - public static interface PositiveNegativePluralModifier { /** - * Converts this {@link PositiveNegativePluralModifier} to a {@link Modifier} given the negative - * sign and the standard plural. + * Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed + * to bubble up. With regard to padding, strong modifiers are considered to be on the inside of the prefix and + * suffix. * - * @param plural The StandardPlural to use. - * @param isNegative true if the negative form of this modifier should be used; false if the - * positive form should be used. - * @return A Modifier corresponding to the negative sign. + * @return Whether the modifier is strong. + */ + public boolean isStrong(); + + /** + * An interface for a modifier that contains both a positive and a negative form. Note that a class implementing + * {@link PositiveNegativeModifier} is not necessarily a {@link Modifier} itself. Rather, it returns a + * {@link Modifier} when {@link #getModifier} is called. + */ + public static interface PositiveNegativeModifier { + /** + * Converts this {@link PositiveNegativeModifier} to a {@link Modifier} given the negative sign. + * + * @param isNegative + * true if the negative form of this modifier should be used; false if the positive form should be + * used. + * @return A Modifier corresponding to the negative sign. + */ + public Modifier getModifier(boolean isNegative); + } + + /** + * An interface for a modifier that contains both a positive and a negative form for all six standard plurals. Note + * that a class implementing {@link PositiveNegativePluralModifier} is not necessarily a {@link Modifier} itself. + * Rather, it returns a {@link Modifier} when {@link #getModifier} is called. */ - public Modifier getModifier(StandardPlural plural, boolean isNegative); - } + public static interface PositiveNegativePluralModifier { + /** + * Converts this {@link PositiveNegativePluralModifier} to a {@link Modifier} given the negative sign and the + * standard plural. + * + * @param plural + * The StandardPlural to use. + * @param isNegative + * true if the negative form of this modifier should be used; false if the positive form should be + * used. + * @return A Modifier corresponding to the negative sign. + */ + public Modifier getModifier(StandardPlural plural, boolean isNegative); + } - /** - * An interface for a modifier that is represented internally by a prefix string and a suffix - * string. - */ - public static interface AffixModifier extends Modifier {} + /** + * An interface for a modifier that is represented internally by a prefix string and a suffix string. + */ + public static interface AffixModifier extends Modifier { + } - /** - * A starter implementation with defaults for some of the basic methods. - * - *

Implements {@link PositiveNegativeModifier} only so that instances of this class can be used - * when a {@link PositiveNegativeModifier} is required. - */ - public abstract static class BaseModifier implements Modifier, PositiveNegativeModifier { + /** + * A starter implementation with defaults for some of the basic methods. + * + *

+ * Implements {@link PositiveNegativeModifier} only so that instances of this class can be used when a + * {@link PositiveNegativeModifier} is required. + */ + public abstract static class BaseModifier implements Modifier, PositiveNegativeModifier { - @Override - public Modifier getModifier(boolean isNegative) { - return this; + @Override + public Modifier getModifier(boolean isNegative) { + return this; + } } - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java index 6b08f9b3685..417e1e0a597 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java @@ -567,8 +567,8 @@ public class PatternString { // The width of the positive prefix and suffix templates are included in the padding int paddingWidth = positive.paddingWidth - + AffixPatternUtils.unescapedLength(posPrefix) - + AffixPatternUtils.unescapedLength(posSuffix); + + AffixPatternUtils.estimateLength(posPrefix) + + AffixPatternUtils.estimateLength(posSuffix); properties.setFormatWidth(paddingWidth); String rawPaddingString = ppr.getString(AffixPatternProvider.Flags.PADDING); if (rawPaddingString.length() == 1) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java index 1b2d88d3245..bc2f9ae3e66 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java @@ -10,82 +10,87 @@ import com.ibm.icu.text.NumberFormat.Field; /** The canonical implementation of {@link Modifier}, containing a prefix and suffix string. */ public class ConstantAffixModifier extends Modifier.BaseModifier implements AffixModifier { - // TODO: Avoid making a new instance by default if prefix and suffix are empty - public static final AffixModifier EMPTY = new ConstantAffixModifier(); + // TODO: Avoid making a new instance by default if prefix and suffix are empty + public static final AffixModifier EMPTY = new ConstantAffixModifier(); - private final String prefix; - private final String suffix; - private final Field field; - private final boolean strong; + private final String prefix; + private final String suffix; + private final Field field; + private final boolean strong; - /** - * Constructs an instance with the given strings. - * - *

The arguments need to be Strings, not CharSequences, because Strings are immutable but - * CharSequences are not. - * - * @param prefix The prefix string. - * @param suffix The suffix string. - * @param field The field type to be associated with this modifier. Can be null. - * @param strong Whether this modifier should be strongly applied. - * @see Field - */ - public ConstantAffixModifier(String prefix, String suffix, Field field, boolean strong) { - // Use an empty string instead of null if we are given null - // TODO: Consider returning a null modifier if both prefix and suffix are empty. - this.prefix = (prefix == null ? "" : prefix); - this.suffix = (suffix == null ? "" : suffix); - this.field = field; - this.strong = strong; - } - - /** Constructs a new instance with an empty prefix, suffix, and field. */ - public ConstantAffixModifier() { - prefix = ""; - suffix = ""; - field = null; - strong = false; - } - - @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { - // Insert the suffix first since inserting the prefix will change the rightIndex - int length = output.insert(rightIndex, suffix, field); - length += output.insert(leftIndex, prefix, field); - return length; - } + /** + * Constructs an instance with the given strings. + * + *

+ * The arguments need to be Strings, not CharSequences, because Strings are immutable but CharSequences are not. + * + * @param prefix + * The prefix string. + * @param suffix + * The suffix string. + * @param field + * The field type to be associated with this modifier. Can be null. + * @param strong + * Whether this modifier should be strongly applied. + * @see Field + */ + public ConstantAffixModifier(String prefix, String suffix, Field field, boolean strong) { + // Use an empty string instead of null if we are given null + // TODO: Consider returning a null modifier if both prefix and suffix are empty. + this.prefix = (prefix == null ? "" : prefix); + this.suffix = (suffix == null ? "" : suffix); + this.field = field; + this.strong = strong; + } - @Override - public boolean isStrong() { - return strong; - } + /** Constructs a new instance with an empty prefix, suffix, and field. */ + public ConstantAffixModifier() { + prefix = ""; + suffix = ""; + field = null; + strong = false; + } - @Override - public String getPrefix() { - return prefix; - } + @Override + public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + // Insert the suffix first since inserting the prefix will change the rightIndex + int length = output.insert(rightIndex, suffix, field); + length += output.insert(leftIndex, prefix, field); + return length; + } - @Override - public String getSuffix() { - return suffix; - } + @Override + public int getPrefixLength() { + return prefix.length(); + } - public boolean contentEquals(CharSequence _prefix, CharSequence _suffix) { - if (_prefix == null && !prefix.isEmpty()) return false; - if (_suffix == null && !suffix.isEmpty()) return false; - if (_prefix != null && prefix.length() != _prefix.length()) return false; - if (_suffix != null && suffix.length() != _suffix.length()) return false; - for (int i = 0; i < prefix.length(); i++) { - if (prefix.charAt(i) != _prefix.charAt(i)) return false; + @Override + public boolean isStrong() { + return strong; } - for (int i = 0; i < suffix.length(); i++) { - if (suffix.charAt(i) != _suffix.charAt(i)) return false; + + public boolean contentEquals(CharSequence _prefix, CharSequence _suffix) { + if (_prefix == null && !prefix.isEmpty()) + return false; + if (_suffix == null && !suffix.isEmpty()) + return false; + if (_prefix != null && prefix.length() != _prefix.length()) + return false; + if (_suffix != null && suffix.length() != _suffix.length()) + return false; + for (int i = 0; i < prefix.length(); i++) { + if (prefix.charAt(i) != _prefix.charAt(i)) + return false; + } + for (int i = 0; i < suffix.length(); i++) { + if (suffix.charAt(i) != _suffix.charAt(i)) + return false; + } + return true; } - return true; - } - @Override - public String toString() { - return String.format("", prefix, suffix); - } + @Override + public String toString() { + return String.format("", prefix, suffix); + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java index c6c4d880501..b2e0164c2b5 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java @@ -8,74 +8,64 @@ import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.text.NumberFormat.Field; /** - * An implementation of {@link Modifier} that allows for multiple types of fields in the same - * modifier. Constructed based on the contents of two {@link NumberStringBuilder} instances (one for - * the prefix, one for the suffix). + * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed + * based on the contents of two {@link NumberStringBuilder} instances (one for the prefix, one for the suffix). */ public class ConstantMultiFieldModifier extends Modifier.BaseModifier implements AffixModifier { - // TODO: Avoid making a new instance by default if prefix and suffix are empty - public static final ConstantMultiFieldModifier EMPTY = new ConstantMultiFieldModifier(); + // TODO: Avoid making a new instance by default if prefix and suffix are empty + public static final ConstantMultiFieldModifier EMPTY = new ConstantMultiFieldModifier(); - protected final char[] prefixChars; - protected final char[] suffixChars; - protected final Field[] prefixFields; - protected final Field[] suffixFields; - private final String prefix; - private final String suffix; - private final boolean strong; + protected final char[] prefixChars; + protected final char[] suffixChars; + protected final Field[] prefixFields; + protected final Field[] suffixFields; + private final boolean strong; - public ConstantMultiFieldModifier( - NumberStringBuilder prefix, NumberStringBuilder suffix, boolean strong) { - prefixChars = prefix.toCharArray(); - suffixChars = suffix.toCharArray(); - prefixFields = prefix.toFieldArray(); - suffixFields = suffix.toFieldArray(); - this.prefix = new String(prefixChars); - this.suffix = new String(suffixChars); - this.strong = strong; - } + public ConstantMultiFieldModifier(NumberStringBuilder prefix, NumberStringBuilder suffix, boolean strong) { + prefixChars = prefix.toCharArray(); + suffixChars = suffix.toCharArray(); + prefixFields = prefix.toFieldArray(); + suffixFields = suffix.toFieldArray(); + this.strong = strong; + } - private ConstantMultiFieldModifier() { - prefixChars = new char[0]; - suffixChars = new char[0]; - prefixFields = new Field[0]; - suffixFields = new Field[0]; - prefix = ""; - suffix = ""; - strong = false; - } + private ConstantMultiFieldModifier() { + prefixChars = new char[0]; + suffixChars = new char[0]; + prefixFields = new Field[0]; + suffixFields = new Field[0]; + strong = false; + } - @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { - // Insert the suffix first since inserting the prefix will change the rightIndex - int length = output.insert(rightIndex, suffixChars, suffixFields); - length += output.insert(leftIndex, prefixChars, prefixFields); - return length; - } + @Override + public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + // Insert the suffix first since inserting the prefix will change the rightIndex + int length = output.insert(rightIndex, suffixChars, suffixFields); + length += output.insert(leftIndex, prefixChars, prefixFields); + return length; + } - @Override - public boolean isStrong() { - return strong; - } + @Override + public int getPrefixLength() { + return prefixChars.length; + } - @Override - public String getPrefix() { - return prefix; - } + @Override + public boolean isStrong() { + return strong; + } - @Override - public String getSuffix() { - return suffix; - } + public boolean contentEquals(NumberStringBuilder prefix, NumberStringBuilder suffix) { + return prefix.contentEquals(prefixChars, prefixFields) && suffix.contentEquals(suffixChars, suffixFields); + } - public boolean contentEquals(NumberStringBuilder prefix, NumberStringBuilder suffix) { - return prefix.contentEquals(prefixChars, prefixFields) - && suffix.contentEquals(suffixChars, suffixFields); - } - - @Override - public String toString() { - return String.format("", prefix, suffix); - } + @Override + public String toString() { + NumberStringBuilder temp = new NumberStringBuilder(); + apply(temp, 0, 0); + int prefixLength = getPrefixLength(); + return String.format("", temp.subSequence(0, prefixLength), + temp.subSequence(prefixLength, temp.length())); + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java index c1ceb221d64..b88b112ad44 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java @@ -8,123 +8,109 @@ import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.text.NumberFormat.Field; /** - * The second primary implementation of {@link Modifier}, this one consuming a {@link - * com.ibm.icu.text.SimpleFormatter} pattern. + * The second primary implementation of {@link Modifier}, this one consuming a {@link com.ibm.icu.text.SimpleFormatter} + * pattern. */ public class SimpleModifier extends Modifier.BaseModifier { - private final String compiledPattern; - private final Field field; - private final boolean strong; + private final String compiledPattern; + private final Field field; + private final boolean strong; - private final int prefixLength; - private final int suffixOffset; - private final int suffixLength; + private final int prefixLength; + private final int suffixOffset; + private final int suffixLength; - private static final int ARG_NUM_LIMIT = 0x100; + private static final int ARG_NUM_LIMIT = 0x100; - /** Creates a modifier that uses the SimpleFormatter string formats. */ - public SimpleModifier(String compiledPattern, Field field, boolean strong) { - this.compiledPattern = (compiledPattern == null) ? "\u0001\u0000" : compiledPattern; - this.field = field; - this.strong = strong; + /** Creates a modifier that uses the SimpleFormatter string formats. */ + public SimpleModifier(String compiledPattern, Field field, boolean strong) { + this.compiledPattern = (compiledPattern == null) ? "\u0001\u0000" : compiledPattern; + this.field = field; + this.strong = strong; - assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1; - if (compiledPattern.charAt(1) != '\u0000') { - prefixLength = compiledPattern.charAt(1) - ARG_NUM_LIMIT; - suffixOffset = 3 + prefixLength; - } else { - prefixLength = 0; - suffixOffset = 2; + assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1; + if (compiledPattern.charAt(1) != '\u0000') { + prefixLength = compiledPattern.charAt(1) - ARG_NUM_LIMIT; + suffixOffset = 3 + prefixLength; + } else { + prefixLength = 0; + suffixOffset = 2; + } + if (3 + prefixLength < compiledPattern.length()) { + suffixLength = compiledPattern.charAt(suffixOffset) - ARG_NUM_LIMIT; + } else { + suffixLength = 0; + } } - if (3 + prefixLength < compiledPattern.length()) { - suffixLength = compiledPattern.charAt(suffixOffset) - ARG_NUM_LIMIT; - } else { - suffixLength = 0; - } - } - - @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { - return formatAsPrefixSuffix(output, leftIndex, rightIndex, field); - } - @Override - public boolean isStrong() { - return strong; - } - - @Override - public String getPrefix() { - if (prefixLength == 0) { - return ""; + @Override + public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + return formatAsPrefixSuffix(output, leftIndex, rightIndex, field); } - return compiledPattern.substring(2, 2 + prefixLength); - } - @Override - public String getSuffix() { - if (suffixLength == 0) { - return ""; + @Override + public int getPrefixLength() { + return prefixLength; } - return compiledPattern.substring(1 + suffixOffset, 1 + suffixOffset + suffixLength); - } - /** - * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is - * because DoubleSidedStringBuilder is an internal class and SimpleFormatterImpl feels like it - * should not depend on it. - * - *

Formats a value that is already stored inside the StringBuilder result between - * the indices startIndex and endIndex by inserting characters before - * the start index and after the end index. - * - *

This is well-defined only for patterns with exactly one argument. - * - * @param result The StringBuilder containing the value argument. - * @param startIndex The left index of the value within the string builder. - * @param endIndex The right index of the value within the string builder. - * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. - */ - public int formatAsPrefixSuffix( - NumberStringBuilder result, int startIndex, int endIndex, Field field) { - assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1; - if (prefixLength > 0) { - result.insert(startIndex, compiledPattern, 2, 2 + prefixLength, field); + @Override + public boolean isStrong() { + return strong; } - if (suffixLength > 0) { - result.insert( - endIndex + prefixLength, - compiledPattern, - 1 + suffixOffset, - 1 + suffixOffset + suffixLength, - field); + + /** + * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because + * DoubleSidedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. + * + *

+ * Formats a value that is already stored inside the StringBuilder result between the indices + * startIndex and endIndex by inserting characters before the start index and after the + * end index. + * + *

+ * This is well-defined only for patterns with exactly one argument. + * + * @param result + * The StringBuilder containing the value argument. + * @param startIndex + * The left index of the value within the string builder. + * @param endIndex + * The right index of the value within the string builder. + * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. + */ + public int formatAsPrefixSuffix(NumberStringBuilder result, int startIndex, int endIndex, Field field) { + assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1; + if (prefixLength > 0) { + result.insert(startIndex, compiledPattern, 2, 2 + prefixLength, field); + } + if (suffixLength > 0) { + result.insert(endIndex + prefixLength, compiledPattern, 1 + suffixOffset, 1 + suffixOffset + suffixLength, + field); + } + return prefixLength + suffixLength; } - return prefixLength + suffixLength; - } - /** TODO: Move this to a test file somewhere, once we figure out what to do with the method. */ - public static void testFormatAsPrefixSuffix() { - String[] patterns = {"{0}", "X{0}Y", "XX{0}YYY", "{0}YY", "XXXX{0}"}; - Object[][] outputs = {{"", 0, 0}, {"abcde", 0, 0}, {"abcde", 2, 2}, {"abcde", 1, 3}}; - String[][] expecteds = { - {"", "XY", "XXYYY", "YY", "XXXX"}, - {"abcde", "XYabcde", "XXYYYabcde", "YYabcde", "XXXXabcde"}, - {"abcde", "abXYcde", "abXXYYYcde", "abYYcde", "abXXXXcde"}, - {"abcde", "aXbcYde", "aXXbcYYYde", "abcYYde", "aXXXXbcde"} - }; - for (int i = 0; i < patterns.length; i++) { - for (int j = 0; j < outputs.length; j++) { - String pattern = patterns[i]; - String compiledPattern = - SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, new StringBuilder(), 1, 1); - NumberStringBuilder output = new NumberStringBuilder(); - output.append((String) outputs[j][0], null); - new SimpleModifier(compiledPattern, null, false) - .apply(output, (Integer) outputs[j][1], (Integer) outputs[j][2]); - String expected = expecteds[j][i]; - String actual = output.toString(); - assert expected.equals(actual); - } + /** TODO: Move this to a test file somewhere, once we figure out what to do with the method. */ + public static void testFormatAsPrefixSuffix() { + String[] patterns = { "{0}", "X{0}Y", "XX{0}YYY", "{0}YY", "XXXX{0}" }; + Object[][] outputs = { { "", 0, 0 }, { "abcde", 0, 0 }, { "abcde", 2, 2 }, { "abcde", 1, 3 } }; + String[][] expecteds = { { "", "XY", "XXYYY", "YY", "XXXX" }, + { "abcde", "XYabcde", "XXYYYabcde", "YYabcde", "XXXXabcde" }, + { "abcde", "abXYcde", "abXXYYYcde", "abYYcde", "abXXXXcde" }, + { "abcde", "aXbcYde", "aXXbcYYYde", "abcYYde", "aXXXXbcde" } }; + for (int i = 0; i < patterns.length; i++) { + for (int j = 0; j < outputs.length; j++) { + String pattern = patterns[i]; + String compiledPattern = SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, + new StringBuilder(), 1, 1); + NumberStringBuilder output = new NumberStringBuilder(); + output.append((String) outputs[j][0], null); + new SimpleModifier(compiledPattern, null, false).apply(output, (Integer) outputs[j][1], + (Integer) outputs[j][2]); + String expected = expecteds[j][i]; + String actual = output.toString(); + assert expected.equals(actual); + } + } } - } } diff --git a/icu4j/main/classes/core/src/newapi/FormattedNumber.java b/icu4j/main/classes/core/src/newapi/FormattedNumber.java index 3749eebc15e..33f432361d6 100644 --- a/icu4j/main/classes/core/src/newapi/FormattedNumber.java +++ b/icu4j/main/classes/core/src/newapi/FormattedNumber.java @@ -69,7 +69,13 @@ public class FormattedNumber { */ @Deprecated public String getPrefix() { - return micros.modOuter.getPrefix() + micros.modMiddle.getPrefix() + micros.modInner.getPrefix(); + NumberStringBuilder temp = new NumberStringBuilder(); + int length = micros.modOuter.apply(temp, 0, 0); + length += micros.modMiddle.apply(temp, 0, length); + length += micros.modInner.apply(temp, 0, length); + int prefixLength = micros.modOuter.getPrefixLength() + micros.modMiddle.getPrefixLength() + + micros.modInner.getPrefixLength(); + return temp.subSequence(0, prefixLength).toString(); } /** @@ -78,7 +84,13 @@ public class FormattedNumber { */ @Deprecated public String getSuffix() { - return micros.modInner.getSuffix() + micros.modMiddle.getSuffix() + micros.modOuter.getSuffix(); + NumberStringBuilder temp = new NumberStringBuilder(); + int length = micros.modOuter.apply(temp, 0, 0); + length += micros.modMiddle.apply(temp, 0, length); + length += micros.modInner.apply(temp, 0, length); + int prefixLength = micros.modOuter.getPrefixLength() + micros.modMiddle.getPrefixLength() + + micros.modInner.getPrefixLength(); + return temp.subSequence(prefixLength, length).toString(); } /** diff --git a/icu4j/main/classes/core/src/newapi/MurkyModifier.java b/icu4j/main/classes/core/src/newapi/MurkyModifier.java index 2ef3c2f911d..f71e15ffbcf 100644 --- a/icu4j/main/classes/core/src/newapi/MurkyModifier.java +++ b/icu4j/main/classes/core/src/newapi/MurkyModifier.java @@ -320,22 +320,13 @@ public class MurkyModifier implements Modifier, SymbolProvider, CharSequence, Qu } @Override - public boolean isStrong() { - return isStrong; + public int getPrefixLength() { + return insertPrefix(null, 0); } @Override - public String getPrefix() { - NumberStringBuilder sb = new NumberStringBuilder(10); - insertPrefix(sb, 0); - return sb.toString(); - } - - @Override - public String getSuffix() { - NumberStringBuilder sb = new NumberStringBuilder(10); - insertSuffix(sb, 0); - return sb.toString(); + public boolean isStrong() { + return isStrong; } private int insertPrefix(NumberStringBuilder sb, int position) { diff --git a/icu4j/main/classes/core/src/newapi/ScientificNotation.java b/icu4j/main/classes/core/src/newapi/ScientificNotation.java index d3a6ea1faf6..3c2214c98b8 100644 --- a/icu4j/main/classes/core/src/newapi/ScientificNotation.java +++ b/icu4j/main/classes/core/src/newapi/ScientificNotation.java @@ -140,22 +140,14 @@ public class ScientificNotation extends Notation implements Cloneable { } @Override - public boolean isStrong() { - return true; - } - - @Override - public String getPrefix() { + public int getPrefixLength() { // FIXME: Localized exponent separator location. - return ""; + return 0; } @Override - public String getSuffix() { - // FIXME: Localized exponent separator location. - NumberStringBuilder temp = new NumberStringBuilder(); - doApply(exponent, temp, 0); - return temp.toString(); + public boolean isStrong() { + return true; } @Override @@ -191,27 +183,19 @@ public class ScientificNotation extends Notation implements Cloneable { } @Override - public boolean isStrong() { - return true; - } - - @Override - public String getPrefix() { - // FIXME: Localized exponent separator location. - return ""; + public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + return doApply(exponent, output, rightIndex); } @Override - public String getSuffix() { + public int getPrefixLength() { // FIXME: Localized exponent separator location. - NumberStringBuilder temp = new NumberStringBuilder(); - doApply(exponent, temp, 0); - return temp.toString(); + return 0; } @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { - return doApply(exponent, output, rightIndex); + public boolean isStrong() { + return true; } } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java index 32bcc71e04f..c0552fda28b 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java @@ -87,7 +87,7 @@ public class AffixPatternUtilsTest { assertEquals( "Currency on <" + input + ">", curr, AffixPatternUtils.hasCurrencySymbols(input)); - assertEquals("Length on <" + input + ">", length, AffixPatternUtils.unescapedLength(input)); + assertEquals("Length on <" + input + ">", length, AffixPatternUtils.estimateLength(input)); String actual = unescapeWithDefaults(input); assertEquals("Output on <" + input + ">", output, actual); @@ -134,7 +134,7 @@ public class AffixPatternUtilsTest { // OK } try { - AffixPatternUtils.unescapedLength(str); + AffixPatternUtils.estimateLength(str); fail("No exception was thrown on an invalid string"); } catch (IllegalArgumentException e) { // OK diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MurkyModifierTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MurkyModifierTest.java index f9cfe3f9076..686bd4a4c5c 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MurkyModifierTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MurkyModifierTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; import com.ibm.icu.impl.number.LdmlPatternInfo; +import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.MeasureFormat.FormatWidth; import com.ibm.icu.util.Currency; @@ -28,31 +29,43 @@ public class MurkyModifierTest { FormatWidth.SHORT, null); murky.setNumberProperties(false, null); - assertEquals("a", murky.getPrefix()); - assertEquals("b", murky.getSuffix()); + assertEquals("a", getPrefix(murky)); + assertEquals("b", getSuffix(murky)); murky.setPatternAttributes(SignDisplay.ALWAYS, false); - assertEquals("+a", murky.getPrefix()); - assertEquals("b", murky.getSuffix()); + assertEquals("+a", getPrefix(murky)); + assertEquals("b", getSuffix(murky)); murky.setNumberProperties(true, null); - assertEquals("-a", murky.getPrefix()); - assertEquals("b", murky.getSuffix()); + assertEquals("-a", getPrefix(murky)); + assertEquals("b", getSuffix(murky)); murky.setPatternAttributes(SignDisplay.NEVER, false); - assertEquals("a", murky.getPrefix()); - assertEquals("b", murky.getSuffix()); + assertEquals("a", getPrefix(murky)); + assertEquals("b", getSuffix(murky)); murky.setPatternInfo(LdmlPatternInfo.parse("a0b;c-0d")); murky.setPatternAttributes(SignDisplay.AUTO, false); murky.setNumberProperties(false, null); - assertEquals("a", murky.getPrefix()); - assertEquals("b", murky.getSuffix()); + assertEquals("a", getPrefix(murky)); + assertEquals("b", getSuffix(murky)); murky.setPatternAttributes(SignDisplay.ALWAYS, false); - assertEquals("c+", murky.getPrefix()); - assertEquals("d", murky.getSuffix()); + assertEquals("c+", getPrefix(murky)); + assertEquals("d", getSuffix(murky)); murky.setNumberProperties(true, null); - assertEquals("c-", murky.getPrefix()); - assertEquals("d", murky.getSuffix()); + assertEquals("c-", getPrefix(murky)); + assertEquals("d", getSuffix(murky)); murky.setPatternAttributes(SignDisplay.NEVER, false); - assertEquals("c-", murky.getPrefix()); // TODO: What should this behavior be? - assertEquals("d", murky.getSuffix()); + assertEquals("c-", getPrefix(murky)); // TODO: What should this behavior be? + assertEquals("d", getSuffix(murky)); + } + + private static String getPrefix(MurkyModifier murky) { + NumberStringBuilder nsb = new NumberStringBuilder(); + murky.apply(nsb, 0, 0); + return nsb.subSequence(0, murky.getPrefixLength()).toString(); + } + + private static String getSuffix(MurkyModifier murky) { + NumberStringBuilder nsb = new NumberStringBuilder(); + murky.apply(nsb, 0, 0); + return nsb.subSequence(murky.getPrefixLength(), nsb.length()).toString(); } }