ICU-20709 Adding fourth signum type. Converting Java to use enum.

This commit is contained in:
Shane Carr 2019-09-27 16:57:52 -07:00 committed by Shane F. Carr
parent e94657e614
commit 369e67221c
22 changed files with 333 additions and 124 deletions

View File

@ -319,10 +319,14 @@ bool DecimalQuantity::isNegative() const {
}
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;
}

View File

@ -308,7 +308,7 @@ void LongNameHandler::simpleFormatsToModifiers(const UnicodeString *simpleFormat
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});
}
}
@ -325,7 +325,7 @@ void LongNameHandler::multiSimpleFormatsToModifiers(const UnicodeString *leadFor
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});
}
}

View File

@ -319,12 +319,12 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
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;
}
};

View File

@ -81,8 +81,10 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
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));
}
@ -95,8 +97,10 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
// 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)) {
@ -263,7 +267,12 @@ int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int pos
/** 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 {

View File

@ -1000,23 +1000,19 @@ PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFo
}
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;
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.)
// TODO: Deal with signum
bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && (
signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
// 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;
@ -1035,8 +1031,8 @@ void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider&
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;
}
@ -1065,4 +1061,54 @@ void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider&
}
}
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 */

View File

@ -22,6 +22,18 @@ namespace impl {
// 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;
@ -295,10 +307,12 @@ class U_I18N_API PatternStringUtils {
* 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,

View File

@ -92,9 +92,10 @@ enum CompactType {
};
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
};

View File

@ -271,8 +271,6 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
// 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;
@ -281,13 +279,23 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
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++]
@ -296,13 +304,13 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
// 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)) {
@ -311,17 +319,17 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
}
// 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};
}
}

View File

@ -2114,7 +2114,7 @@ void NumberFormatterApiTest::signCoverage() {
{ 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[] = {

View File

@ -41,7 +41,10 @@ void PatternModifierTest::testBasic() {
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);
@ -66,7 +69,10 @@ void PatternModifierTest::testBasic() {
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);
@ -76,9 +82,8 @@ void PatternModifierTest::testBasic() {
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);
}

View File

@ -3,6 +3,7 @@
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.
@ -11,7 +12,8 @@ import com.ibm.icu.impl.StandardPlural;
*/
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;
@ -22,9 +24,10 @@ public class AdoptingModifierStore implements ModifierStore {
* <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;
@ -39,13 +42,14 @@ public class AdoptingModifierStore implements ModifierStore {
*/
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;
}
@ -54,21 +58,34 @@ public class AdoptingModifierStore implements ModifierStore {
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();
}
}

View File

@ -7,6 +7,7 @@ import java.math.MathContext;
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;
@ -130,8 +131,8 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
/** @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

View File

@ -9,6 +9,7 @@ import java.text.FieldPosition;
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;
@ -303,8 +304,18 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
}
@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

View File

@ -12,6 +12,7 @@ import com.ibm.icu.impl.ICUResourceBundle;
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;
@ -262,7 +263,7 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
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));
}
@ -281,7 +282,7 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
.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));
}
@ -296,7 +297,8 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
}
@Override
public Modifier getModifier(int signum, StandardPlural plural) {
public Modifier getModifier(Signum signum, StandardPlural plural) {
// Signum ignored
return modifiers.get(plural);
}
}

View File

@ -17,6 +17,13 @@ import com.ibm.icu.impl.StandardPlural;
*/
public interface Modifier {
static enum Signum {
NEG,
NEG_ZERO,
POS_ZERO,
POS
};
/**
* Apply this Modifier to the string builder.
*
@ -65,7 +72,7 @@ public interface Modifier {
*/
public static class Parameters {
public ModifierStore obj;
public int signum;
public Signum signum;
public StandardPlural plural;
}

View File

@ -3,6 +3,7 @@
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
@ -14,5 +15,5 @@ public interface ModifierStore {
/**
* Returns a Modifier with the given parameters (best-effort).
*/
Modifier getModifier(int signum, StandardPlural plural);
Modifier getModifier(Signum signum, StandardPlural plural);
}

View File

@ -50,7 +50,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
PluralRules rules;
// Number details
int signum;
Signum signum;
StandardPlural plural;
// QuantityChain details
@ -129,7 +129,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
* 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;
@ -174,24 +174,28 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
// 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);
}
}
@ -367,8 +371,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
}
PatternStringUtils.patternInfoToStringBuilder(patternInfo,
isPrefix,
signum,
signDisplay,
PatternStringUtils.resolveSignDisplay(signDisplay, signum),
plural,
perMilleReplacesPercent,
currentAffix);

View File

@ -5,6 +5,7 @@ package com.ibm.icu.impl.number;
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;
@ -14,6 +15,18 @@ 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
@ -23,7 +36,7 @@ public class PatternStringUtils {
* 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.
*
@ -416,25 +429,19 @@ public class PatternStringUtils {
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;
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.)
// 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;
@ -453,8 +460,8 @@ public class PatternStringUtils {
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;
}
@ -483,4 +490,53 @@ public class PatternStringUtils {
}
}
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");
}
}

View File

@ -12,7 +12,7 @@ import com.ibm.icu.impl.StringSegment;
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
@ -90,20 +90,27 @@ public class AffixMatcher implements NumberParseMatcher {
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);
@ -113,15 +120,14 @@ public class AffixMatcher implements NumberParseMatcher {
// 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)) {
@ -130,17 +136,17 @@ public class AffixMatcher implements NumberParseMatcher {
}
// 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));
}
}

View File

@ -9,6 +9,7 @@ import java.text.FieldPosition;
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;
@ -517,8 +518,18 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
}
@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) {

View File

@ -12,6 +12,7 @@ import com.ibm.icu.impl.FormattedStringBuilder;
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;
@ -32,19 +33,22 @@ public class MutablePatternModifierTest {
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);
@ -53,24 +57,27 @@ public class MutablePatternModifierTest {
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
@ -112,7 +119,7 @@ public class MutablePatternModifierTest {
Currency.getInstance("USD"),
UnitWidth.SHORT,
null);
mod.setNumberProperties(1, null);
mod.setNumberProperties(Signum.POS_ZERO, null);
// Unsafe Code Path
FormattedStringBuilder nsb = new FormattedStringBuilder();

View File

@ -2036,7 +2036,7 @@ public class NumberFormatterApiTest {
{ {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[] {