}
Signum DecimalQuantity::signum() const {
- if (isNegative()) {
+ bool isZero = (isZeroish() && !isInfinite());
+ bool isNeg = isNegative();
+ if (isZero && isNeg) {
+ return SIGNUM_NEG_ZERO;
+ } else if (isZero) {
+ return SIGNUM_POS_ZERO;
+ } else if (isNeg) {
return SIGNUM_NEG;
- } else if (isZeroish() && !isInfinite()) {
- return SIGNUM_ZERO;
} else {
return SIGNUM_POS;
}
if (U_FAILURE(status)) { return; }
SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status);
if (U_FAILURE(status)) { return; }
- fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_ZERO, plural});
+ fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_POS_ZERO, plural});
}
}
if (U_FAILURE(status)) { return; }
SimpleFormatter compoundCompiled(compoundFormat, 0, 1, status);
if (U_FAILURE(status)) { return; }
- fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_ZERO, plural});
+ fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_POS_ZERO, plural});
}
}
private:
// NOTE: mods is zero-initialized (to nullptr)
- const Modifier *mods[3 * StandardPlural::COUNT] = {};
+ const Modifier *mods[4 * StandardPlural::COUNT] = {};
inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) {
- U_ASSERT(signum >= -1 && signum <= 1);
+ U_ASSERT(signum >= 0 && signum <= 3);
U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT);
- return static_cast<int32_t>(plural) * 3 + (signum + 1);
+ return static_cast<int32_t>(plural) * 4 + signum;
}
};
for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
setNumberProperties(SIGNUM_POS, plural);
pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status));
- setNumberProperties(SIGNUM_ZERO, plural);
- pm->adoptModifier(SIGNUM_ZERO, plural, createConstantModifier(status));
+ setNumberProperties(SIGNUM_NEG_ZERO, plural);
+ pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status));
+ setNumberProperties(SIGNUM_POS_ZERO, plural);
+ pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG, plural);
pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
}
// Faster path when plural keyword is not needed.
setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
- setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT);
- pm->adoptModifierWithoutPlural(SIGNUM_ZERO, createConstantModifier(status));
+ setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
+ pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status));
+ setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
+ pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
if (U_FAILURE(status)) {
/** This method contains the heart of the logic for rendering LDML affix strings. */
void MutablePatternModifier::prepareAffix(bool isPrefix) {
PatternStringUtils::patternInfoToStringBuilder(
- *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, fPerMilleReplacesPercent, currentAffix);
+ *fPatternInfo,
+ isPrefix,
+ PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum),
+ fPlural,
+ fPerMilleReplacesPercent,
+ currentAffix);
}
UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
}
void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
- Signum signum, UNumberSignDisplay signDisplay,
+ PatternSignType patternSignType,
StandardPlural::Form plural,
bool perMilleReplacesPercent, UnicodeString& output) {
// Should the output render '+' where '-' would normally appear in the pattern?
- bool plusReplacesMinusSign = signum != -1 && (
- signDisplay == UNUM_SIGN_ALWAYS || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS || (
- signum == 1 && (
- signDisplay == UNUM_SIGN_EXCEPT_ZERO ||
- signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) &&
- patternInfo.positiveHasPlusSign() == false;
-
- // Should we use the affix from the negative subpattern? (If not, we will use the positive
- // subpattern.)
- // TODO: Deal with signum
- bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && (
- signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
+ bool plusReplacesMinusSign = (patternSignType == PATTERN_SIGN_TYPE_POS_SIGN)
+ && !patternInfo.positiveHasPlusSign();
+
+ // Should we use the affix from the negative subpattern?
+ // (If not, we will use the positive subpattern.)
+ bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern()
+ && (patternSignType == PATTERN_SIGN_TYPE_NEG
+ || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
// Resolve the flags for the affix pattern.
int flags = 0;
bool prependSign;
if (!isPrefix || useNegativeAffixPattern) {
prependSign = false;
- } else if (signum == -1) {
- prependSign = signDisplay != UNUM_SIGN_NEVER;
+ } else if (patternSignType == PATTERN_SIGN_TYPE_NEG) {
+ prependSign = true;
} else {
prependSign = plusReplacesMinusSign;
}
}
}
+PatternSignType PatternStringUtils::resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum) {
+ switch (signDisplay) {
+ case UNUM_SIGN_AUTO:
+ case UNUM_SIGN_ACCOUNTING:
+ switch (signum) {
+ case SIGNUM_NEG:
+ case SIGNUM_NEG_ZERO:
+ return PATTERN_SIGN_TYPE_NEG;
+ case SIGNUM_POS_ZERO:
+ case SIGNUM_POS:
+ return PATTERN_SIGN_TYPE_POS;
+ }
+ break;
+
+ case UNUM_SIGN_ALWAYS:
+ case UNUM_SIGN_ACCOUNTING_ALWAYS:
+ switch (signum) {
+ case SIGNUM_NEG:
+ case SIGNUM_NEG_ZERO:
+ return PATTERN_SIGN_TYPE_NEG;
+ case SIGNUM_POS_ZERO:
+ case SIGNUM_POS:
+ return PATTERN_SIGN_TYPE_POS_SIGN;
+ }
+ break;
+
+ case UNUM_SIGN_EXCEPT_ZERO:
+ case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO:
+ switch (signum) {
+ case SIGNUM_NEG:
+ return PATTERN_SIGN_TYPE_NEG;
+ case SIGNUM_NEG_ZERO:
+ case SIGNUM_POS_ZERO:
+ return PATTERN_SIGN_TYPE_POS;
+ case SIGNUM_POS:
+ return PATTERN_SIGN_TYPE_POS_SIGN;
+ }
+ break;
+
+ case UNUM_SIGN_NEVER:
+ return PATTERN_SIGN_TYPE_POS;
+
+ default:
+ break;
+ }
+
+ UPRV_UNREACHABLE;
+ return PATTERN_SIGN_TYPE_POS;
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
// Forward declaration
class PatternParser;
+// Note: the order of fields in this enum matters for parsing.
+enum PatternSignType {
+ /** Render using normal positive subpattern rules */
+ PATTERN_SIGN_TYPE_POS,
+ /** Render using rules to force the display of a plus sign */
+ PATTERN_SIGN_TYPE_POS_SIGN,
+ /** Render using negative subpattern rules */
+ PATTERN_SIGN_TYPE_NEG,
+ /** Count for looping over the possibilities */
+ PATTERN_SIGN_TYPE_COUNT
+};
+
// Exported as U_I18N_API because it is a public member field of exported ParsedSubpatternInfo
struct U_I18N_API Endpoints {
int32_t start = 0;
* substitution, and plural forms for CurrencyPluralInfo.
*/
static void patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
- Signum signum, UNumberSignDisplay signDisplay,
+ PatternSignType patternSignType,
StandardPlural::Form plural, bool perMilleReplacesPercent,
UnicodeString& output);
+ static PatternSignType resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum);
+
private:
/** @return The number of chars inserted. */
static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex,
};
enum Signum {
- SIGNUM_NEG = -1,
- SIGNUM_ZERO = 0,
- SIGNUM_POS = 1
+ SIGNUM_NEG = 0,
+ SIGNUM_NEG_ZERO = 1,
+ SIGNUM_POS_ZERO = 2,
+ SIGNUM_POS = 3
};
// Use initial capacity of 6, the highest possible number of AffixMatchers.
UnicodeString sb;
bool includeUnpaired = 0 != (parseFlags & PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES);
- UNumberSignDisplay signDisplay = (0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) ? UNUM_SIGN_ALWAYS
- : UNUM_SIGN_AUTO;
int32_t numAffixMatchers = 0;
int32_t numAffixPatternMatchers = 0;
AffixPatternMatcher* posSuffix = nullptr;
// Pre-process the affix strings to resolve LDML rules like sign display.
- for (int8_t signumInt = 1; signumInt >= -1; signumInt--) {
- auto signum = static_cast<Signum>(signumInt);
+ for (int8_t typeInt = 0; typeInt < PATTERN_SIGN_TYPE_COUNT; typeInt++) {
+ auto type = static_cast<PatternSignType>(typeInt);
+
+ // Skip affixes in some cases
+ if (type == PATTERN_SIGN_TYPE_POS
+ && 0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) {
+ continue;
+ }
+ if (type == PATTERN_SIGN_TYPE_POS_SIGN
+ && 0 == (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) {
+ continue;
+ }
// Generate Prefix
bool hasPrefix = false;
PatternStringUtils::patternInfoToStringBuilder(
- patternInfo, true, signum, signDisplay, StandardPlural::OTHER, false, sb);
+ patternInfo, true, type, StandardPlural::OTHER, false, sb);
fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern(
sb, *fTokenWarehouse, parseFlags, &hasPrefix, status);
AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++]
// Generate Suffix
bool hasSuffix = false;
PatternStringUtils::patternInfoToStringBuilder(
- patternInfo, false, signum, signDisplay, StandardPlural::OTHER, false, sb);
+ patternInfo, false, type, StandardPlural::OTHER, false, sb);
fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern(
sb, *fTokenWarehouse, parseFlags, &hasSuffix, status);
AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++]
: nullptr;
- if (signum == 1) {
+ if (type == PATTERN_SIGN_TYPE_POS) {
posPrefix = prefix;
posSuffix = suffix;
} else if (equals(prefix, posPrefix) && equals(suffix, posSuffix)) {
}
// Flags for setting in the ParsedNumber; the token matchers may add more.
- int flags = (signum == -1) ? FLAG_NEGATIVE : 0;
+ int flags = (type == PATTERN_SIGN_TYPE_NEG) ? FLAG_NEGATIVE : 0;
// Note: it is indeed possible for posPrefix and posSuffix to both be null.
// We still need to add that matcher for strict mode to work.
fAffixMatchers[numAffixMatchers++] = {prefix, suffix, flags};
if (includeUnpaired && prefix != nullptr && suffix != nullptr) {
// The following if statements are designed to prevent adding two identical matchers.
- if (signum == 1 || !equals(prefix, posPrefix)) {
+ if (type == PATTERN_SIGN_TYPE_POS || !equals(prefix, posPrefix)) {
fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags};
}
- if (signum == 1 || !equals(suffix, posSuffix)) {
+ if (type == PATTERN_SIGN_TYPE_POS || !equals(suffix, posSuffix)) {
fAffixMatchers[numAffixMatchers++] = {nullptr, suffix, flags};
}
}
{ UNUM_SIGN_AUTO, { u"-∞", u"-1", u"-0", u"0", u"1", u"∞", u"NaN", u"-NaN" } },
{ UNUM_SIGN_ALWAYS, { u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
{ UNUM_SIGN_NEVER, { u"∞", u"1", u"0", u"0", u"1", u"∞", u"NaN", u"NaN" } },
- { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"-0", u"0", u"+1", u"+∞", u"NaN", u"-NaN" } },
+ { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"0", u"0", u"+1", u"+∞", u"NaN", u"NaN" } },
};
double negNaN = std::copysign(uprv_getNaN(), -0.0);
const double inputs[] = {
mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false);
assertEquals("Pattern a0b", u"+a", getPrefix(mod, status));
assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
- mod.setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
+ assertEquals("Pattern a0b", u"-a", getPrefix(mod, status));
+ assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
+ mod.setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
assertEquals("Pattern a0b", u"+a", getPrefix(mod, status));
assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
mod.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false);
mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false);
assertEquals("Pattern a0b;c-0d", u"c+", getPrefix(mod, status));
assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
- mod.setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
+ assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status));
+ assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
+ mod.setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
assertEquals("Pattern a0b;c-0d", u"c+", getPrefix(mod, status));
assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
mod.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false);
assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status));
assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
mod.setPatternAttributes(UNUM_SIGN_NEVER, false);
- // TODO: What should this behavior be?
- assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status));
- assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
+ assertEquals("Pattern a0b;c-0d", u"a", getPrefix(mod, status));
+ assertEquals("Pattern a0b;c-0d", u"b", getSuffix(mod, status));
assertSuccess("Spot 5", status);
}
package com.ibm.icu.impl.number;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.Modifier.Signum;
/**
* This implementation of ModifierStore adopts references to Modifiers.
*/
public class AdoptingModifierStore implements ModifierStore {
private final Modifier positive;
- private final Modifier zero;
+ private final Modifier posZero;
+ private final Modifier negZero;
private final Modifier negative;
final Modifier[] mods;
boolean frozen;
* <p>
* If this constructor is used, a plural form CANNOT be passed to {@link #getModifier}.
*/
- public AdoptingModifierStore(Modifier positive, Modifier zero, Modifier negative) {
+ public AdoptingModifierStore(Modifier positive, Modifier posZero, Modifier negZero, Modifier negative) {
this.positive = positive;
- this.zero = zero;
+ this.posZero = posZero;
+ this.negZero = negZero;
this.negative = negative;
this.mods = null;
this.frozen = true;
*/
public AdoptingModifierStore() {
this.positive = null;
- this.zero = null;
+ this.posZero = null;
+ this.negZero = null;
this.negative = null;
- this.mods = new Modifier[3 * StandardPlural.COUNT];
+ this.mods = new Modifier[4 * StandardPlural.COUNT];
this.frozen = false;
}
- public void setModifier(int signum, StandardPlural plural, Modifier mod) {
+ public void setModifier(Signum signum, StandardPlural plural, Modifier mod) {
assert !frozen;
mods[getModIndex(signum, plural)] = mod;
}
frozen = true;
}
- public Modifier getModifierWithoutPlural(int signum) {
+ public Modifier getModifierWithoutPlural(Signum signum) {
assert frozen;
assert mods == null;
- return signum == 0 ? zero : signum < 0 ? negative : positive;
+ assert signum != null;
+ switch (signum) {
+ case POS:
+ return positive;
+ case POS_ZERO:
+ return posZero;
+ case NEG_ZERO:
+ return negZero;
+ case NEG:
+ return negative;
+ default:
+ throw new AssertionError("Unreachable");
+ }
}
- public Modifier getModifier(int signum, StandardPlural plural) {
+ @Override
+ public Modifier getModifier(Signum signum, StandardPlural plural) {
assert frozen;
assert positive == null;
return mods[getModIndex(signum, plural)];
}
- private static int getModIndex(int signum, StandardPlural plural) {
- assert signum >= -1 && signum <= 1;
+ private static int getModIndex(Signum signum, StandardPlural plural) {
+ assert signum != null;
assert plural != null;
- return plural.ordinal() * 3 + (signum + 1);
+ return plural.ordinal() * 4 + signum.ordinal();
}
}
import java.text.FieldPosition;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.UFieldPosition;
/** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
public boolean isNegative();
- /** @return -1 if the value is negative; 1 if positive; or 0 if zero. */
- public int signum();
+ /** @return The appropriate value from the Signum enum. */
+ public Signum signum();
/** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
@Override
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.Utility;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.PluralRules.Operand;
import com.ibm.icu.text.UFieldPosition;
}
@Override
- public int signum() {
- return isNegative() ? -1 : (isZeroish() && !isInfinite()) ? 0 : 1;
+ public Signum signum() {
+ boolean isZero = (isZeroish() && !isInfinite());
+ boolean isNeg = isNegative();
+ if (isZero && isNeg) {
+ return Signum.NEG_ZERO;
+ } else if (isZero) {
+ return Signum.POS_ZERO;
+ } else if (isNeg) {
+ return Signum.NEG;
+ } else {
+ return Signum.POS;
+ }
}
@Override
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.UResource;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.number.NumberFormatter.UnitWidth;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.PluralRules;
String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 0, 1);
Modifier.Parameters parameters = new Modifier.Parameters();
parameters.obj = this;
- parameters.signum = 0;
+ parameters.signum = null;// Signum ignored
parameters.plural = plural;
modifiers.put(plural, new SimpleModifier(compiled, field, false, parameters));
}
.compileToStringMinMaxArguments(compoundFormat, sb, 0, 1);
Modifier.Parameters parameters = new Modifier.Parameters();
parameters.obj = this;
- parameters.signum = 0;
+ parameters.signum = null; // Signum ignored
parameters.plural = plural;
modifiers.put(plural, new SimpleModifier(compoundCompiled, field, false, parameters));
}
}
@Override
- public Modifier getModifier(int signum, StandardPlural plural) {
+ public Modifier getModifier(Signum signum, StandardPlural plural) {
+ // Signum ignored
return modifiers.get(plural);
}
}
*/
public interface Modifier {
+ static enum Signum {
+ NEG,
+ NEG_ZERO,
+ POS_ZERO,
+ POS
+ };
+
/**
* Apply this Modifier to the string builder.
*
*/
public static class Parameters {
public ModifierStore obj;
- public int signum;
+ public Signum signum;
public StandardPlural plural;
}
package com.ibm.icu.impl.number;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.Modifier.Signum;
/**
* This is *not* a modifier; rather, it is an object that can return modifiers
/**
* Returns a Modifier with the given parameters (best-effort).
*/
- Modifier getModifier(int signum, StandardPlural plural);
+ Modifier getModifier(Signum signum, StandardPlural plural);
}
PluralRules rules;
// Number details
- int signum;
+ Signum signum;
StandardPlural plural;
// QuantityChain details
* The plural form of the number, required only if the pattern contains the triple
* currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}).
*/
- public void setNumberProperties(int signum, StandardPlural plural) {
+ public void setNumberProperties(Signum signum, StandardPlural plural) {
assert (plural != null) == needsPlurals();
this.signum = signum;
this.plural = plural;
// Slower path when we require the plural keyword.
AdoptingModifierStore pm = new AdoptingModifierStore();
for (StandardPlural plural : StandardPlural.VALUES) {
- setNumberProperties(1, plural);
- pm.setModifier(1, plural, createConstantModifier(a, b));
- setNumberProperties(0, plural);
- pm.setModifier(0, plural, createConstantModifier(a, b));
- setNumberProperties(-1, plural);
- pm.setModifier(-1, plural, createConstantModifier(a, b));
+ setNumberProperties(Signum.POS, plural);
+ pm.setModifier(Signum.POS, plural, createConstantModifier(a, b));
+ setNumberProperties(Signum.POS_ZERO, plural);
+ pm.setModifier(Signum.POS_ZERO, plural, createConstantModifier(a, b));
+ setNumberProperties(Signum.NEG_ZERO, plural);
+ pm.setModifier(Signum.NEG_ZERO, plural, createConstantModifier(a, b));
+ setNumberProperties(Signum.NEG, plural);
+ pm.setModifier(Signum.NEG, plural, createConstantModifier(a, b));
}
pm.freeze();
return new ImmutablePatternModifier(pm, rules, parent);
} else {
// Faster path when plural keyword is not needed.
- setNumberProperties(1, null);
+ setNumberProperties(Signum.POS, null);
Modifier positive = createConstantModifier(a, b);
- setNumberProperties(0, null);
- Modifier zero = createConstantModifier(a, b);
- setNumberProperties(-1, null);
+ setNumberProperties(Signum.POS_ZERO, null);
+ Modifier posZero = createConstantModifier(a, b);
+ setNumberProperties(Signum.NEG_ZERO, null);
+ Modifier negZero = createConstantModifier(a, b);
+ setNumberProperties(Signum.NEG, null);
Modifier negative = createConstantModifier(a, b);
- AdoptingModifierStore pm = new AdoptingModifierStore(positive, zero, negative);
+ AdoptingModifierStore pm = new AdoptingModifierStore(positive, posZero, negZero, negative);
return new ImmutablePatternModifier(pm, null, parent);
}
}
}
PatternStringUtils.patternInfoToStringBuilder(patternInfo,
isPrefix,
- signum,
- signDisplay,
+ PatternStringUtils.resolveSignDisplay(signDisplay, signum),
plural,
perMilleReplacesPercent,
currentAffix);
import java.math.BigDecimal;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.number.NumberFormatter.SignDisplay;
import com.ibm.icu.text.DecimalFormatSymbols;
*/
public class PatternStringUtils {
+ // Note: the order of fields in this enum matters for parsing.
+ public static enum PatternSignType {
+ // Render using normal positive subpattern rules
+ POS,
+ // Render using rules to force the display of a plus sign
+ POS_SIGN,
+ // Render using negative subpattern rules
+ NEG;
+
+ public static final PatternSignType[] VALUES = PatternSignType.values();
+ };
+
/**
* Determine whether a given roundingIncrement should be ignored for formatting
* based on the current maxFrac value (maximum fraction digits). For example a
* it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of
* 0.005 is treated like 0.001 for significance).
*
- * This test is needed for both NumberPropertyMapper.oldToNew and
+ * This test is needed for both NumberPropertyMapper.oldToNew and
* PatternStringUtils.propertiesToPatternString, but NumberPropertyMapper
* is package-private so we have it here.
*
public static void patternInfoToStringBuilder(
AffixPatternProvider patternInfo,
boolean isPrefix,
- int signum,
- SignDisplay signDisplay,
+ PatternSignType patternSignType,
StandardPlural plural,
boolean perMilleReplacesPercent,
StringBuilder output) {
- // Should the output render '+' where '-' would normally appear in the pattern?
- boolean plusReplacesMinusSign = signum != -1
- && (signDisplay == SignDisplay.ALWAYS
- || signDisplay == SignDisplay.ACCOUNTING_ALWAYS
- || (signum == 1
- && (signDisplay == SignDisplay.EXCEPT_ZERO
- || signDisplay == SignDisplay.ACCOUNTING_EXCEPT_ZERO)))
- && patternInfo.positiveHasPlusSign() == false;
-
- // Should we use the affix from the negative subpattern? (If not, we will use the positive
- // subpattern.)
+ boolean plusReplacesMinusSign = (patternSignType == PatternSignType.POS_SIGN)
+ && !patternInfo.positiveHasPlusSign();
+
+ // Should we use the affix from the negative subpattern?
+ // (If not, we will use the positive subpattern.)
boolean useNegativeAffixPattern = patternInfo.hasNegativeSubpattern()
- && (signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
+ && (patternSignType == PatternSignType.NEG
+ || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
// Resolve the flags for the affix pattern.
int flags = 0;
boolean prependSign;
if (!isPrefix || useNegativeAffixPattern) {
prependSign = false;
- } else if (signum == -1) {
- prependSign = signDisplay != SignDisplay.NEVER;
+ } else if (patternSignType == PatternSignType.NEG) {
+ prependSign = true;
} else {
prependSign = plusReplacesMinusSign;
}
}
}
+ public static PatternSignType resolveSignDisplay(SignDisplay signDisplay, Signum signum) {
+ switch (signDisplay) {
+ case AUTO:
+ case ACCOUNTING:
+ switch (signum) {
+ case NEG:
+ case NEG_ZERO:
+ return PatternSignType.NEG;
+ case POS_ZERO:
+ case POS:
+ return PatternSignType.POS;
+ }
+ break;
+
+ case ALWAYS:
+ case ACCOUNTING_ALWAYS:
+ switch (signum) {
+ case NEG:
+ case NEG_ZERO:
+ return PatternSignType.NEG;
+ case POS_ZERO:
+ case POS:
+ return PatternSignType.POS_SIGN;
+ }
+ break;
+
+ case EXCEPT_ZERO:
+ case ACCOUNTING_EXCEPT_ZERO:
+ switch (signum) {
+ case NEG:
+ return PatternSignType.NEG;
+ case NEG_ZERO:
+ case POS_ZERO:
+ return PatternSignType.POS;
+ case POS:
+ return PatternSignType.POS_SIGN;
+ }
+ break;
+
+ case NEVER:
+ return PatternSignType.POS;
+
+ default:
+ break;
+ }
+
+ throw new AssertionError("Unreachable");
+ }
+
}
import com.ibm.icu.impl.number.AffixPatternProvider;
import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.PatternStringUtils;
-import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.impl.number.PatternStringUtils.PatternSignType;
/**
* @author sffc
StringBuilder sb = new StringBuilder();
ArrayList<AffixMatcher> matchers = new ArrayList<>(6);
boolean includeUnpaired = 0 != (parseFlags & ParsingUtils.PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES);
- SignDisplay signDisplay = (0 != (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED))
- ? SignDisplay.ALWAYS
- : SignDisplay.AUTO;
AffixPatternMatcher posPrefix = null;
AffixPatternMatcher posSuffix = null;
// Pre-process the affix strings to resolve LDML rules like sign display.
- for (int signum = 1; signum >= -1; signum--) {
+ for (PatternSignType type : PatternSignType.VALUES) {
+
+ // Skip affixes in some cases
+ if (type == PatternSignType.POS
+ && 0 != (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED)) {
+ continue;
+ }
+ if (type == PatternSignType.POS_SIGN
+ && 0 == (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED)) {
+ continue;
+ }
+
// Generate Prefix
PatternStringUtils.patternInfoToStringBuilder(patternInfo,
true,
- signum,
- signDisplay,
+ type,
StandardPlural.OTHER,
false,
sb);
// Generate Suffix
PatternStringUtils.patternInfoToStringBuilder(patternInfo,
false,
- signum,
- signDisplay,
+ type,
StandardPlural.OTHER,
false,
sb);
AffixPatternMatcher suffix = AffixPatternMatcher
.fromAffixPattern(sb.toString(), factory, parseFlags);
- if (signum == 1) {
+ if (type == PatternSignType.POS) {
posPrefix = prefix;
posSuffix = suffix;
} else if (Objects.equals(prefix, posPrefix) && Objects.equals(suffix, posSuffix)) {
}
// Flags for setting in the ParsedNumber; the token matchers may add more.
- int flags = (signum == -1) ? ParsedNumber.FLAG_NEGATIVE : 0;
+ int flags = (type == PatternSignType.NEG) ? ParsedNumber.FLAG_NEGATIVE : 0;
// Note: it is indeed possible for posPrefix and posSuffix to both be null.
// We still need to add that matcher for strict mode to work.
matchers.add(getInstance(prefix, suffix, flags));
if (includeUnpaired && prefix != null && suffix != null) {
// The following if statements are designed to prevent adding two identical matchers.
- if (signum == 1 || !Objects.equals(prefix, posPrefix)) {
+ if (type == PatternSignType.POS || !Objects.equals(prefix, posPrefix)) {
matchers.add(getInstance(prefix, null, flags));
}
- if (signum == 1 || !Objects.equals(suffix, posSuffix)) {
+ if (type == PatternSignType.POS || !Objects.equals(suffix, posSuffix)) {
matchers.add(getInstance(null, suffix, flags));
}
}
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.PluralRules.Operand;
import com.ibm.icu.text.UFieldPosition;
}
@Override
- public int signum() {
- return isNegative() ? -1 : isZeroish() ? 0 : 1;
+ public Signum signum() {
+ boolean isZero = (isZeroish() && !isInfinite());
+ boolean isNeg = isNegative();
+ if (isZero && isNeg) {
+ return Signum.NEG_ZERO;
+ } else if (isZero) {
+ return Signum.POS_ZERO;
+ } else if (isNeg) {
+ return Signum.NEG;
+ } else {
+ return Signum.POS;
+ }
}
private void setNegative(boolean isNegative) {
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.MicroProps;
+import com.ibm.icu.impl.number.Modifier.Signum;
import com.ibm.icu.impl.number.MutablePatternModifier;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.number.NumberFormatter.SignDisplay;
UnitWidth.SHORT,
null);
- mod.setNumberProperties(1, null);
+ mod.setNumberProperties(Signum.POS, null);
assertEquals("a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.ALWAYS, false);
assertEquals("+a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
- mod.setNumberProperties(0, null);
+ mod.setNumberProperties(Signum.POS_ZERO, null);
assertEquals("+a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
+ mod.setNumberProperties(Signum.NEG_ZERO, null);
+ assertEquals("-a", getPrefix(mod));
+ assertEquals("b", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.EXCEPT_ZERO, false);
assertEquals("a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
- mod.setNumberProperties(-1, null);
+ mod.setNumberProperties(Signum.NEG, null);
assertEquals("-a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.NEVER, false);
mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"), null);
mod.setPatternAttributes(SignDisplay.AUTO, false);
- mod.setNumberProperties(1, null);
+ mod.setNumberProperties(Signum.POS, null);
assertEquals("a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.ALWAYS, false);
assertEquals("c+", getPrefix(mod));
assertEquals("d", getSuffix(mod));
- mod.setNumberProperties(0, null);
+ mod.setNumberProperties(Signum.POS_ZERO, null);
assertEquals("c+", getPrefix(mod));
assertEquals("d", getSuffix(mod));
+ mod.setNumberProperties(Signum.NEG_ZERO, null);
+ assertEquals("c-", getPrefix(mod));
+ assertEquals("d", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.EXCEPT_ZERO, false);
assertEquals("a", getPrefix(mod));
assertEquals("b", getSuffix(mod));
- mod.setNumberProperties(-1, null);
+ mod.setNumberProperties(Signum.NEG, null);
assertEquals("c-", getPrefix(mod));
assertEquals("d", getSuffix(mod));
mod.setPatternAttributes(SignDisplay.NEVER, false);
- assertEquals("c-", getPrefix(mod)); // TODO: What should this behavior be?
- assertEquals("d", getSuffix(mod));
+ assertEquals("a", getPrefix(mod));
+ assertEquals("b", getSuffix(mod));
}
@Test
Currency.getInstance("USD"),
UnitWidth.SHORT,
null);
- mod.setNumberProperties(1, null);
+ mod.setNumberProperties(Signum.POS_ZERO, null);
// Unsafe Code Path
FormattedStringBuilder nsb = new FormattedStringBuilder();
{ {SignDisplay.AUTO}, { "-∞", "-1", "-0", "0", "1", "∞", "NaN", "-NaN" } },
{ {SignDisplay.ALWAYS}, { "-∞", "-1", "-0", "+0", "+1", "+∞", "+NaN", "-NaN" } },
{ {SignDisplay.NEVER}, { "∞", "1", "0", "0", "1", "∞", "NaN", "NaN" } },
- { {SignDisplay.EXCEPT_ZERO}, { "-∞", "-1", "-0", "0", "+1", "+∞", "NaN", "-NaN" } },
+ { {SignDisplay.EXCEPT_ZERO}, { "-∞", "-1", "0", "0", "+1", "+∞", "NaN", "NaN" } },
};
double negNaN = Math.copySign(Double.NaN, -0.0);
double inputs[] = new double[] {