blob: 26cb275c5f7db41c37f3f548be7628dc685c7e3a [file] [log] [blame]
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number;
import java.text.Format.Field;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.number.range.PrefixInfixSuffixLengthHelper;
import com.ibm.icu.util.ICUException;
/**
* The second primary implementation of {@link Modifier}, this one consuming a
* {@link com.ibm.icu.text.SimpleFormatter} pattern.
*/
public class SimpleModifier implements Modifier {
private final String compiledPattern;
private final Field field;
private final boolean strong;
private final int prefixLength;
private final int suffixOffset;
private final int suffixLength;
// Parameters: used for number range formatting
private final Parameters parameters;
/** TODO: This is copied from SimpleFormatterImpl. */
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, field, strong, null);
}
/** Creates a modifier that uses the SimpleFormatter string formats. */
public SimpleModifier(String compiledPattern, Field field, boolean strong, Parameters parameters) {
assert compiledPattern != null;
this.compiledPattern = compiledPattern;
this.field = field;
this.strong = strong;
this.parameters = parameters;
int argLimit = SimpleFormatterImpl.getArgumentLimit(compiledPattern);
if (argLimit == 0) {
// No arguments in compiled pattern
prefixLength = compiledPattern.charAt(1) - ARG_NUM_LIMIT;
assert 2 + prefixLength == compiledPattern.length();
// Set suffixOffset = -1 to indicate no arguments in compiled pattern.
suffixOffset = -1;
suffixLength = 0;
} else {
assert argLimit == 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;
}
}
}
@Override
public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
return formatAsPrefixSuffix(output, leftIndex, rightIndex);
}
@Override
public int getPrefixLength() {
return prefixLength;
}
@Override
public int getCodePointCount() {
int count = 0;
if (prefixLength > 0) {
count += Character.codePointCount(compiledPattern, 2, 2 + prefixLength);
}
if (suffixLength > 0) {
count += Character
.codePointCount(compiledPattern, 1 + suffixOffset, 1 + suffixOffset + suffixLength);
}
return count;
}
@Override
public boolean isStrong() {
return strong;
}
@Override
public boolean containsField(Field field) {
// This method is not currently used.
assert false;
return false;
}
@Override
public Parameters getParameters() {
return parameters;
}
@Override
public boolean semanticallyEquivalent(Modifier other) {
if (!(other instanceof SimpleModifier)) {
return false;
}
SimpleModifier _other = (SimpleModifier) other;
if (parameters != null && _other.parameters != null && parameters.obj == _other.parameters.obj) {
return true;
}
return compiledPattern.equals(_other.compiledPattern) && field == _other.field && strong == _other.strong;
}
/**
* 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) {
if (suffixOffset == -1) {
// There is no argument for the inner number; overwrite the entire segment with our string.
return result.splice(startIndex, endIndex, compiledPattern, 2, 2 + prefixLength, field);
} else {
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;
}
}
/**
* TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code.
* I put it here so that the SimpleFormatter uses in NumberStringBuilder are near each other.
*
* <p>
* Applies the compiled two-argument pattern to the NumberStringBuilder.
*
* <p>
* This method is optimized for the case where the prefix and suffix are often empty, such as
* in the range pattern like "{0}-{1}".
*/
public static void formatTwoArgPattern(String compiledPattern, NumberStringBuilder result, int index, PrefixInfixSuffixLengthHelper h,
Field field) {
int argLimit = SimpleFormatterImpl.getArgumentLimit(compiledPattern);
if (argLimit != 2) {
throw new ICUException();
}
int offset = 1; // offset into compiledPattern
int length = 0; // chars added to result
int prefixLength = compiledPattern.charAt(offset);
offset++;
if (prefixLength < ARG_NUM_LIMIT) {
// No prefix
prefixLength = 0;
} else {
prefixLength -= ARG_NUM_LIMIT;
result.insert(index + length, compiledPattern, offset, offset + prefixLength, field);
offset += prefixLength;
length += prefixLength;
offset++;
}
int infixLength = compiledPattern.charAt(offset);
offset++;
if (infixLength < ARG_NUM_LIMIT) {
// No infix
infixLength = 0;
} else {
infixLength -= ARG_NUM_LIMIT;
result.insert(index + length, compiledPattern, offset, offset + infixLength, field);
offset += infixLength;
length += infixLength;
offset++;
}
int suffixLength;
if (offset == compiledPattern.length()) {
// No suffix
suffixLength = 0;
} else {
suffixLength = compiledPattern.charAt(offset) - ARG_NUM_LIMIT;
offset++;
result.insert(index + length, compiledPattern, offset, offset + suffixLength, field);
length += suffixLength;
}
h.lengthPrefix = prefixLength;
h.lengthInfix = infixLength;
h.lengthSuffix = suffixLength;
}
}