]> granicus.if.org Git - icu/commitdiff
ICU-13177 Simplifying the Modifier interface by removing getPrefix() and getSuffix...
authorShane Carr <shane@unicode.org>
Sat, 26 Aug 2017 07:22:38 +0000 (07:22 +0000)
committerShane Carr <shane@unicode.org>
Sat, 26 Aug 2017 07:22:38 +0000 (07:22 +0000)
X-SVN-Rev: 40358

icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java
icu4j/main/classes/core/src/newapi/FormattedNumber.java
icu4j/main/classes/core/src/newapi/MurkyModifier.java
icu4j/main/classes/core/src/newapi/ScientificNotation.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MurkyModifierTest.java

index c49ad5ecdf5d29d131c1ed0064c542928f85540b..56c824c08ced057f11eccc5b68391dfac814032b 100644 (file)
@@ -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();
   }
 
   /**
index 10b5e05ca00f1402e1689356d1698afb9466e7c4..8bb55e145ddc1e14a20f98230964a3c38090bf3e 100644 (file)
@@ -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.
-   *
-   * <p>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.
-   *
-   * <p>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.
-   *
-   * <p>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.
+     *
+     * <p>
+     * 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;
+        }
     }
-  }
 }
index 6b08f9b368540754cb00eec104260fd987c31d80..417e1e0a5970b18decf970c0141f4dc080e108f8 100644 (file)
@@ -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) {
index 1b2d88d3245991ada41c3a4b6f3e6f0c9866623b..bc2f9ae3e669f8dfafbb76adfe079bc08ae6a368 100644 (file)
@@ -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.
-   *
-   * <p>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.
+     *
+     * <p>
+     * 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("<ConstantAffixModifier prefix:'%s' suffix:'%s'>", prefix, suffix);
-  }
+    @Override
+    public String toString() {
+        return String.format("<ConstantAffixModifier prefix:'%s' suffix:'%s'>", prefix, suffix);
+    }
 }
index c6c4d880501c1bf66016b89db5c054001165d873..b2e0164c2b5d25b686c263253ac490936f038e69 100644 (file)
@@ -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("<ConstantMultiFieldModifier prefix:'%s' suffix:'%s'>", prefix, suffix);
-  }
+    @Override
+    public String toString() {
+        NumberStringBuilder temp = new NumberStringBuilder();
+        apply(temp, 0, 0);
+        int prefixLength = getPrefixLength();
+        return String.format("<ConstantMultiFieldModifier prefix:'%s' suffix:'%s'>", temp.subSequence(0, prefixLength),
+                temp.subSequence(prefixLength, temp.length()));
+    }
 }
index c1ceb221d646725e03681d38b81150605bfc2bea..b88b112ad446cf45dd48db87311f53313a513e30 100644 (file)
@@ -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.
-   *
-   * <p>Formats a value that is already stored inside the StringBuilder <code>result</code> between
-   * the indices <code>startIndex</code> and <code>endIndex</code> by inserting characters before
-   * the start index and after the end index.
-   *
-   * <p>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.
+     *
+     * <p>
+     * Formats a value that is already stored inside the StringBuilder <code>result</code> between the indices
+     * <code>startIndex</code> and <code>endIndex</code> by inserting characters before the start index and after the
+     * end index.
+     *
+     * <p>
+     * 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);
+            }
+        }
     }
-  }
 }
index 3749eebc15e11f44a9437ebe44ef532118e0c331..33f432361d6d498bc132e504d7d0c7ba50908a87 100644 (file)
@@ -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();
     }
 
     /**
index 2ef3c2f911de3996e295c7b291e11ccb7b9de358..f71e15ffbcf8744555777457281d512701f69c6c 100644 (file)
@@ -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) {
index d3a6ea1faf6a0b95f237c73bb3db8355a7fa8912..3c2214c98b85bb6859d63a59d7c5ed43b0229eef 100644 (file)
@@ -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;
             }
         }
     }
index 32bcc71e04ff9c63a35f8db9fcfb526e20bb9018..c0552fda28b2b6ba28c10aec57f1a94f1d2463cd 100644 (file)
@@ -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
index f9cfe3f90760eff53b11f984e87964532234bd51..686bd4a4c5c854b26af2f69ccd2a4bda0c8d7061 100644 (file)
@@ -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();
   }
 }