UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
ErrorCode localStatus;
- fFormatter->getAffix(true, false, result, localStatus);
+ fFormatter->getAffixImpl(true, false, result, localStatus);
return result;
}
UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
ErrorCode localStatus;
- fFormatter->getAffix(true, true, result, localStatus);
+ fFormatter->getAffixImpl(true, true, result, localStatus);
return result;
}
UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
ErrorCode localStatus;
- fFormatter->getAffix(false, false, result, localStatus);
+ fFormatter->getAffixImpl(false, false, result, localStatus);
return result;
}
UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
ErrorCode localStatus;
- fFormatter->getAffix(false, true, result, localStatus);
+ fFormatter->getAffixImpl(false, true, result, localStatus);
return result;
}
}
void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const {
+ if (computeCompiled(status)) {
+ fCompiled->apply(results->quantity, results->string, status);
+ } else {
+ NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
+ }
+}
+
+void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result,
+ UErrorCode& status) const {
+ NumberStringBuilder string;
+ auto signum = static_cast<int8_t>(isNegative ? -1 : 1);
+ // Always return affixes for plural form OTHER.
+ static const StandardPlural::Form plural = StandardPlural::OTHER;
+ int32_t prefixLength;
+ if (computeCompiled(status)) {
+ prefixLength = fCompiled->getPrefixSuffix(signum, plural, string, status);
+ } else {
+ prefixLength = NumberFormatterImpl::getPrefixSuffixStatic(fMacros, signum, plural, string, status);
+ }
+ result.remove();
+ if (isPrefix) {
+ result.append(string.toTempUnicodeString().tempSubStringBetween(0, prefixLength));
+ } else {
+ result.append(string.toTempUnicodeString().tempSubStringBetween(prefixLength, string.length()));
+ }
+}
+
+bool LocalizedNumberFormatter::computeCompiled(UErrorCode& status) const {
// fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
// std::atomic<int32_t>. Since the type of atomic int is platform-dependent, we cast the
// bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
U_ASSERT(fCompiled == nullptr);
const_cast<LocalizedNumberFormatter*>(this)->fCompiled = compiled;
umtx_storeRelease(*callCount, INT32_MIN);
- compiled->apply(results->quantity, results->string, status);
+ return true;
} else if (currentCount < 0) {
// The data structure is already built; use it (fast path).
U_ASSERT(fCompiled != nullptr);
- fCompiled->apply(results->quantity, results->string, status);
+ return true;
} else {
// Format the number without building the data structure (slow path).
- NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
- }
-}
-
-void LocalizedNumberFormatter::getAffix(bool isPrefix, bool isNegative, UnicodeString& result,
- UErrorCode& status) const {
- NumberStringBuilder nsb;
- DecimalQuantity dq;
- if (isNegative) {
- dq.setToInt(-1);
- } else {
- dq.setToInt(1);
- }
- int prefixLength = NumberFormatterImpl::getPrefixSuffix(fMacros, dq, nsb, status);
- result.remove();
- if (isPrefix) {
- result.append(nsb.toTempUnicodeString().tempSubStringBetween(0, prefixLength));
- } else {
- result.append(nsb.toTempUnicodeString().tempSubStringBetween(prefixLength, nsb.length()));
+ return false;
}
}
impl.applyUnsafe(inValue, outString, status);
}
-int32_t NumberFormatterImpl::getPrefixSuffix(const MacroProps& macros, DecimalQuantity& inValue,
- NumberStringBuilder& outString, UErrorCode& status) {
+int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
+ StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
- return impl.getPrefixSuffixUnsafe(inValue, outString, status);
+ return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
}
// NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
microsToString(fMicros, inValue, outString, status);
}
-int32_t
-NumberFormatterImpl::getPrefixSuffixUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
- UErrorCode& status) {
+int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) const {
if (U_FAILURE(status)) { return 0; }
- fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
+ // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
+ // Safe path: use fImmutablePatternModifier.
+ const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
+ modifier->apply(outString, 0, 0, status);
+ if (U_FAILURE(status)) { return 0; }
+ return modifier->getPrefixLength(status);
+}
+
+int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status) {
if (U_FAILURE(status)) { return 0; }
- // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
- fMicros.modMiddle->apply(outString, 0, 0, status);
+ // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
+ // Unsafe path: use fPatternModifier.
+ fPatternModifier->setNumberProperties(signum, plural);
+ fPatternModifier->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
- return fMicros.modMiddle->getPrefixLength(status);
+ return fPatternModifier->getPrefixLength(status);
}
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
- static int32_t getPrefixSuffix(const MacroProps& macros, DecimalQuantity& inValue,
- NumberStringBuilder& outString, UErrorCode& status);
+ static int32_t getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
+ StandardPlural::Form plural, NumberStringBuilder& outString,
+ UErrorCode& status);
/**
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
*/
- void apply(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status) const;
+ void apply(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
+
+ /**
+ * Like getPrefixSuffixStatic() but uses the safe compiled object.
+ */
+ int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, NumberStringBuilder& outString,
+ UErrorCode& status) const;
private:
// Head of the MicroPropsGenerator linked list:
LocalPointer<const PluralRules> fRules;
LocalPointer<const ParsedPatternInfo> fPatternInfo;
LocalPointer<const ScientificHandler> fScientificHandler;
- LocalPointer<const MutablePatternModifier> fPatternModifier;
+ LocalPointer<MutablePatternModifier> fPatternModifier;
LocalPointer<const ImmutablePatternModifier> fImmutablePatternModifier;
LocalPointer<const LongNameHandler> fLongNameHandler;
LocalPointer<const CompactHandler> fCompactHandler;
void applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status);
- int32_t getPrefixSuffixUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
- UErrorCode& status);
+ int32_t getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
+ NumberStringBuilder& outString, UErrorCode& status);
/**
* If rulesPtr is non-null, return it. Otherwise, return a PluralRules owned by this object for the
}
}
+const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const {
+ if (rules == nullptr) {
+ return pm->getModifier(signum);
+ } else {
+ return pm->getModifier(signum, plural);
+ }
+}
+
+
/** Used by the unsafe code path. */
MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
this->parent = parent;
void applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const;
+ const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const;
+
private:
ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
const MicroPropsGenerator* parent);
/** Internal method for DecimalFormat compatibility.
* @internal
*/
- void getAffix(bool isPrefix, bool isNegative, UnicodeString& result, UErrorCode& status) const;
+ void getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, UErrorCode& status) const;
/**
* Internal method for testing.
LocalizedNumberFormatter(impl::MacroProps &¯os, const Locale &locale);
+ /**
+ * @return true if the compiled formatter is available.
+ */
+ bool computeCompiled(UErrorCode& status) const;
+
// To give the fluent setters access to this class's constructor:
friend class NumberFormatterSettings<UnlocalizedNumberFormatter>;
friend class NumberFormatterSettings<LocalizedNumberFormatter>;
micros.modMiddle = pm.getModifier(quantity.signum(), plural);
}
}
+
+ // NOTE: This method is not used in ICU4J right now.
+ // In ICU4C, it is used by getPrefixSuffix().
+ // Un-comment this method when getPrefixSuffix() is cleaned up in ICU4J.
+ // public Modifier getModifier(byte signum, StandardPlural plural) {
+ // if (rules == null) {
+ // return pm.getModifier(signum);
+ // } else {
+ // return pm.getModifier(signum, plural);
+ // }
+ // }
}
/** Used by the unsafe code path. */
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
*/
@Deprecated
public FormattedNumber format(DecimalQuantity fq) {
- MacroProps macros = resolve();
- // NOTE: In Java, the atomic increment logic is slightly different than ICU4C.
- // It seems to be more efficient to make just one function call instead of two.
- // Further benchmarking is required.
- long currentCount = callCount.incrementAndGet(this);
NumberStringBuilder string = new NumberStringBuilder();
- if (currentCount == macros.threshold.longValue()) {
- compiled = NumberFormatterImpl.fromMacros(macros);
- compiled.apply(fq, string);
- } else if (compiled != null) {
+ if (computeCompiled()) {
compiled.apply(fq, string);
} else {
- NumberFormatterImpl.applyStatic(macros, fq, string);
+ NumberFormatterImpl.applyStatic(resolve(), fq, string);
}
return new FormattedNumber(string, fq);
}
* {@link FormattedNumber#getFieldIterator} for similar functionality.
*/
@Deprecated
- public String getAffix(boolean isPrefix, boolean isNegative) {
- MacroProps macros = resolve();
- NumberStringBuilder nsb = new NumberStringBuilder();
- DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(isNegative ? -1 : 1);
- int prefixLength = NumberFormatterImpl.getPrefixSuffix(macros, dq, nsb);
+ public String getAffixImpl(boolean isPrefix, boolean isNegative) {
+ NumberStringBuilder string = new NumberStringBuilder();
+ byte signum = (byte) (isNegative ? -1 : 1);
+ // Always return affixes for plural form OTHER.
+ StandardPlural plural = StandardPlural.OTHER;
+ int prefixLength;
+ if (computeCompiled()) {
+ prefixLength = compiled.getPrefixSuffix(signum, plural, string);
+ } else {
+ prefixLength = NumberFormatterImpl.getPrefixSuffixStatic(resolve(), signum, plural, string);
+ }
if (isPrefix) {
- return nsb.subSequence(0, prefixLength).toString();
+ return string.subSequence(0, prefixLength).toString();
+ } else {
+ return string.subSequence(prefixLength, string.length()).toString();
+ }
+ }
+
+ private boolean computeCompiled() {
+ MacroProps macros = resolve();
+ // NOTE: In Java, the atomic increment logic is slightly different than ICU4C.
+ // It seems to be more efficient to make just one function call instead of two.
+ // Further benchmarking is required.
+ long currentCount = callCount.incrementAndGet(this);
+ if (currentCount == macros.threshold.longValue()) {
+ compiled = NumberFormatterImpl.fromMacros(macros);
+ return true;
+ } else if (compiled != null) {
+ return true;
} else {
- return nsb.subSequence(prefixLength, nsb.length()).toString();
+ return false;
}
}
import com.ibm.icu.impl.CurrencyData;
import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
+import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.number.CompactData.CompactType;
import com.ibm.icu.impl.number.ConstantAffixModifier;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.Grouper;
import com.ibm.icu.impl.number.LongNameHandler;
import com.ibm.icu.impl.number.MacroProps;
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
- public static int getPrefixSuffix(
+ public static int getPrefixSuffixStatic(
MacroProps macros,
- DecimalQuantity inValue,
+ byte signum,
+ StandardPlural plural,
NumberStringBuilder output) {
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, false);
- MicroProps micros = microPropsGenerator.processQuantity(inValue);
- // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
- micros.modMiddle.apply(output, 0, 0);
- return micros.modMiddle.getPrefixLength();
+ return getPrefixSuffixImpl(microPropsGenerator, signum, output);
}
private static final Currency DEFAULT_CURRENCY = Currency.getInstance("XXX");
microsToString(micros, inValue, outString);
}
+ public int getPrefixSuffix(byte signum, StandardPlural plural, NumberStringBuilder output) {
+ return getPrefixSuffixImpl(microPropsGenerator, signum, output);
+ }
+
+ private static int getPrefixSuffixImpl(MicroPropsGenerator generator, byte signum, NumberStringBuilder output) {
+ // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
+ // TODO: Clean this up, closer to C++. The pattern modifier is not as accessible as in C++.
+ // Right now, ignore the plural form, run the pipeline with number 0, and get the modifier from the result.
+ DecimalQuantity_DualStorageBCD quantity = new DecimalQuantity_DualStorageBCD(0);
+ if (signum < 0) {
+ quantity.negate();
+ }
+ MicroProps micros = generator.processQuantity(quantity);
+ micros.modMiddle.apply(output, 0, 0);
+ return micros.modMiddle.getPrefixLength();
+ }
+
//////////
private static boolean unitIsCurrency(MeasureUnit unit) {
* @stable ICU 2.0
*/
public synchronized String getPositivePrefix() {
- return formatter.getAffix(true, false);
+ return formatter.getAffixImpl(true, false);
}
/**
* @stable ICU 2.0
*/
public synchronized String getNegativePrefix() {
- return formatter.getAffix(true, true);
+ return formatter.getAffixImpl(true, true);
}
/**
* @stable ICU 2.0
*/
public synchronized String getPositiveSuffix() {
- return formatter.getAffix(false, false);
+ return formatter.getAffixImpl(false, false);
}
/**
* @stable ICU 2.0
*/
public synchronized String getNegativeSuffix() {
- return formatter.getAffix(false, true);
+ return formatter.getAffixImpl(false, true);
}
/**
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
df.applyPattern("¤¤¤ 0");
String result = df.getPositivePrefix();
- assertEquals("Triple-currency should give long name on getPositivePrefix", "US dollar ", result);
+ assertEquals("Triple-currency should give long name on getPositivePrefix", "US dollars ", result);
}
@Test