#include "unicode/decimfmt.h"
#include "unicode/localpointer.h"
#include "unicode/msgfmt.h"
+#include "unicode/numberformatter.h"
#include "unicode/plurfmt.h"
#include "unicode/rbnf.h"
#include "unicode/selfmt.h"
formattableType = Formattable::kLong;
fmt = createIntegerFormat(fLocale, ec);
break;
- default: // pattern
- fmt = NumberFormat::createInstance(fLocale, ec);
- if (fmt) {
- DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fmt);
- if (decfmt != NULL) {
- decfmt->applyPattern(style,parseError,ec);
+ default: // pattern or skeleton
+ int32_t i = 0;
+ for (; PatternProps::isWhiteSpace(style.charAt(i)); i++);
+ if (style.compare(i, 2, u"::", 0, 2) == 0) {
+ // Skeleton
+ UnicodeString skeleton = style.tempSubString(i + 2);
+ fmt = number::NumberFormatter::fromSkeleton(skeleton, ec).locale(fLocale).toFormat(ec);
+ } else {
+ // Pattern
+ fmt = NumberFormat::createInstance(fLocale, ec);
+ if (fmt) {
+ auto* decfmt = dynamic_cast<DecimalFormat*>(fmt);
+ if (decfmt != nullptr) {
+ decfmt->applyPattern(style, parseError, ec);
+ }
}
}
break;
#include "number_formatimpl.h"
#include "umutex.h"
#include "number_skeletons.h"
+#include "number_utils.h"
#include "number_utypes.h"
#include "util.h"
}
-FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
- : fResults(src.fResults), fErrorCode(src.fErrorCode) {
- // Disown src.fResults to prevent double-deletion
- src.fResults = nullptr;
- src.fErrorCode = U_INVALID_STATE_ERROR;
-}
-
-FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
- delete fResults;
- fResults = src.fResults;
- fErrorCode = src.fErrorCode;
- // Disown src.fResults to prevent double-deletion
- src.fResults = nullptr;
- src.fErrorCode = U_INVALID_STATE_ERROR;
- return *this;
-}
-
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new UFormattedNumberData();
return umtx_loadAcquire(*callCount);
}
+Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const {
+ LocalPointer<LocalizedNumberFormatterAsFormat> retval(
+ new LocalizedNumberFormatterAsFormat(*this, fMacros.locale), status);
+ return retval.orphan();
+}
+
+
+FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
+ : fResults(src.fResults), fErrorCode(src.fErrorCode) {
+ // Disown src.fResults to prevent double-deletion
+ src.fResults = nullptr;
+ src.fErrorCode = U_INVALID_STATE_ERROR;
+}
+
+FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
+ delete fResults;
+ fResults = src.fResults;
+ fErrorCode = src.fErrorCode;
+ // Disown src.fResults to prevent double-deletion
+ src.fResults = nullptr;
+ src.fErrorCode = U_INVALID_STATE_ERROR;
+ return *this;
+}
+
UnicodeString FormattedNumber::toString() const {
UErrorCode localStatus = U_ZERO_ERROR;
return toString(localStatus);
#include "double-conversion.h"
#include "uresimp.h"
#include "ureslocs.h"
+#include "number_utypes.h"
using namespace icu;
using namespace icu::number;
}
+LocalizedNumberFormatterAsFormat::LocalizedNumberFormatterAsFormat(
+ const LocalizedNumberFormatter& formatter, const Locale& locale)
+ : fFormatter(formatter), fLocale(locale) {
+ const char* localeName = locale.getName();
+ setLocaleIDs(localeName, localeName);
+}
+
+LocalizedNumberFormatterAsFormat::~LocalizedNumberFormatterAsFormat() = default;
+
+UBool LocalizedNumberFormatterAsFormat::operator==(const Format& other) const {
+ auto* _other = dynamic_cast<const LocalizedNumberFormatterAsFormat*>(&other);
+ if (_other == nullptr) {
+ return false;
+ }
+ // TODO: Change this to use LocalizedNumberFormatter::operator== if it is ever proposed.
+ // This implementation is fine, but not particularly efficient.
+ UErrorCode localStatus = U_ZERO_ERROR;
+ return fFormatter.toSkeleton(localStatus) == _other->fFormatter.toSkeleton(localStatus);
+}
+
+Format* LocalizedNumberFormatterAsFormat::clone() const {
+ return new LocalizedNumberFormatterAsFormat(*this);
+}
+
+UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo,
+ FieldPosition& pos, UErrorCode& status) const {
+ if (U_FAILURE(status)) { return appendTo; }
+ UFormattedNumberData data;
+ obj.populateDecimalQuantity(data.quantity, status);
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ fFormatter.formatImpl(&data, status);
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ // always return first occurrence:
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+ bool found = data.string.nextFieldPosition(pos, status);
+ if (found && appendTo.length() != 0) {
+ pos.setBeginIndex(pos.getBeginIndex() + appendTo.length());
+ pos.setEndIndex(pos.getEndIndex() + appendTo.length());
+ }
+ appendTo.append(data.string.toTempUnicodeString());
+ return appendTo;
+}
+
+UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo,
+ FieldPositionIterator* posIter,
+ UErrorCode& status) const {
+ if (U_FAILURE(status)) { return appendTo; }
+ UFormattedNumberData data;
+ obj.populateDecimalQuantity(data.quantity, status);
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ fFormatter.formatImpl(&data, status);
+ if (U_FAILURE(status)) {
+ return appendTo;
+ }
+ appendTo.append(data.string.toTempUnicodeString());
+ if (posIter != nullptr) {
+ data.string.getAllFieldPositions(*posIter, status);
+ }
+ return appendTo;
+}
+
+void LocalizedNumberFormatterAsFormat::parseObject(const UnicodeString&, Formattable&,
+ ParsePosition& parse_pos) const {
+ // Not supported.
+ parse_pos.setErrorIndex(0);
+}
+
+const LocalizedNumberFormatter& LocalizedNumberFormatterAsFormat::getNumberFormatter() const {
+ return fFormatter;
+}
+
+
const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style,
UErrorCode& status) {
const char* patternKey;
bool exhausted = false;
};
+
+/**
+ * A wrapper around LocalizedNumberFormatter implementing the Format interface, enabling improved
+ * compatibility with other APIs.
+ *
+ * @draft ICU 62
+ * @see NumberFormatter
+ */
+class U_I18N_API LocalizedNumberFormatterAsFormat : public Format {
+ public:
+ LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale);
+
+ /**
+ * Destructor.
+ */
+ ~LocalizedNumberFormatterAsFormat() U_OVERRIDE;
+
+ /**
+ * Equals operator.
+ */
+ UBool operator==(const Format& other) const U_OVERRIDE;
+
+ /**
+ * Creates a copy of this object.
+ */
+ Format* clone() const U_OVERRIDE;
+
+ /**
+ * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a
+ * number type.
+ */
+ UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPosition& pos,
+ UErrorCode& status) const U_OVERRIDE;
+
+ /**
+ * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a
+ * number type.
+ */
+ UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPositionIterator* posIter,
+ UErrorCode& status) const U_OVERRIDE;
+
+ /**
+ * Not supported: sets an error index and returns.
+ */
+ void parseObject(const UnicodeString& source, Formattable& result,
+ ParsePosition& parse_pos) const U_OVERRIDE;
+
+ /**
+ * Gets the LocalizedNumberFormatter that this wrapper class uses to format numbers.
+ *
+ * For maximum efficiency, this function returns by const reference. You must copy the return value
+ * into a local variable if you want to use it beyond the lifetime of the current object:
+ *
+ * <pre>
+ * LocalizedNumberFormatter localFormatter = fmt->getNumberFormatter();
+ * </pre>
+ *
+ * You can however use the return value directly when chaining:
+ *
+ * <pre>
+ * FormattedNumber result = fmt->getNumberFormatter().formatDouble(514.23, status);
+ * </pre>
+ *
+ * @return The unwrapped LocalizedNumberFormatter.
+ */
+ const LocalizedNumberFormatter& getNumberFormatter() const;
+
+ private:
+ LocalizedNumberFormatter fFormatter;
+
+ // Even though the locale is inside the LocalizedNumberFormatter, we have to keep it here, too, because
+ // LocalizedNumberFormatter doesn't have a getLocale() method, and ICU-TC didn't want to add one.
+ Locale fLocale;
+};
+
+
enum CldrPatternStyle {
CLDR_PATTERN_STYLE_DECIMAL,
CLDR_PATTERN_STYLE_CURRENCY,
#endif
+ /**
+ * Creates a representation of this LocalizedNumberFormat as an icu::Format, enabling the use
+ * of this number formatter with APIs that need an object of that type, such as MessageFormat.
+ *
+ * This API is not intended to be used other than for enabling API compatibility. The formatDouble,
+ * formatInt, and formatDecimal methods should normally be used when formatting numbers, not the Format
+ * object returned by this method.
+ *
+ * The caller owns the returned object and must delete it when finished.
+ *
+ * @return A Format wrapping this LocalizedNumberFormatter.
+ * @draft ICU 62
+ */
+ Format* toFormat(UErrorCode& status) const;
+
/**
* Default constructor: puts the formatter into a valid but undefined state.
*
void locale();
void formatTypes();
void fieldPosition();
+ void toFormat();
void errors();
void validRanges();
void copyMove();
#include "unicode/unum.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
+#include "number_utils.h"
#include "numbertest.h"
#include "unicode/utypes.h"
TESTCASE_AUTO(locale);
TESTCASE_AUTO(formatTypes);
TESTCASE_AUTO(fieldPosition);
+ TESTCASE_AUTO(toFormat);
TESTCASE_AUTO(errors);
TESTCASE_AUTO(validRanges);
TESTCASE_AUTO(copyMove);
assertFalse(u"No fraction part in an integer", fmtd.nextFieldPosition(actual, status));
}
+void NumberFormatterApiTest::toFormat() {
+ IcuTestErrorCode status(*this, "icuFormat");
+ LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
+ .precision(Precision::fixedFraction(3));
+ LocalPointer<Format> format(lnf.toFormat(status), status);
+ FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
+ UnicodeString sb;
+ format->format(514.23, sb, fpos, status);
+ assertEquals("Should correctly format number", u"514,230", sb);
+ assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
+ assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
+ assertEquals(
+ "ICU Format should round-trip",
+ lnf.toSkeleton(status),
+ dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
+ .toSkeleton(status));
+
+ FieldPositionIterator fpi1;
+ lnf.formatDouble(514.23, status).getAllFieldPositions(fpi1, status);
+ FieldPositionIterator fpi2;
+ format->format(514.23, sb.remove(), &fpi2, status);
+ assertTrue("Should produce same field position iterator", fpi1 == fpi2);
+}
+
void NumberFormatterApiTest::errors() {
LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
Precision::fixedFraction(
TESTCASE_AUTO(TestSelectOrdinal);
TESTCASE_AUTO(TestDecimals);
TESTCASE_AUTO(TestArgIsPrefixOfAnother);
+ TESTCASE_AUTO(TestMessageFormatNumberSkeleton);
TESTCASE_AUTO_END;
}
assertEquals("aa aaa", "AB ABC", mf3.format(argNames + 1, args + 1, 2, result.remove(), errorCode));
}
+void TestMessageFormat::TestMessageFormatNumberSkeleton() {
+ IcuTestErrorCode status(*this, "TestMessageFormatNumberSkeleton");
+
+ static const struct TestCase {
+ const char16_t* messagePattern;
+ const char* localeName;
+ double arg;
+ const char16_t* expected;
+ } cases[] = {
+ { u"{0,number,::percent}", "en", 50, u"50%" },
+ { u"{0,number,::percent scale/100}", "en", 0.5, u"50%" },
+ { u"{0,number, :: percent scale/100 }", "en", 0.5, u"50%" },
+ { u"{0,number,::currency/USD}", "en", 23, u"$23.00" },
+ { u"{0,number,::precision-integer}", "en", 514.23, u"514" },
+ { u"{0,number,::.000}", "en", 514.23, u"514.230" },
+ { u"{0,number,::.}", "en", 514.23, u"514" },
+ { u"{0,number,::}", "fr", 514.23, u"514,23" },
+ { u"Cost: {0,number,::currency/EUR}.", "en", 4.3, u"Cost: €4.30." },
+ { u"{0,number,'::'0.00}", "en", 50, u"::50.00" }, // pattern literal
+ };
+
+ for (auto& cas : cases) {
+ status.setScope(cas.messagePattern);
+ MessageFormat msgf(cas.messagePattern, cas.localeName, status);
+ UnicodeString sb;
+ FieldPosition fpos(0);
+ Formattable argsArray[] = {{cas.arg}};
+ Formattable args(argsArray, 1);
+ msgf.format(args, sb, status);
+
+ assertEquals(cas.messagePattern, cas.expected, sb);
+ }
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
void TestSelectOrdinal();
void TestDecimals();
void TestArgIsPrefixOfAnother();
+ void TestMessageFormatNumberSkeleton();
private:
UnicodeString GetPatternAndSkipSyntax(const MessagePattern& pattern);
--- /dev/null
+// © 2018 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.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectStreamException;
+import java.text.AttributedCharacterIterator;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+
+import com.ibm.icu.number.FormattedNumber;
+import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.NumberFormatter;
+import com.ibm.icu.util.ULocale;
+
+/**
+ * A wrapper around LocalizedNumberFormatter implementing the Format interface, enabling improved
+ * compatibility with other APIs. This class is serializable.
+ */
+public class LocalizedNumberFormatterAsFormat extends Format {
+
+ private final transient LocalizedNumberFormatter formatter;
+
+ // Even though the locale is inside the LocalizedNumberFormatter, we have to keep it here, too, because
+ // LocalizedNumberFormatter doesn't have a getLocale() method, and ICU-TC didn't want to add one.
+ private final transient ULocale locale;
+
+ public LocalizedNumberFormatterAsFormat(LocalizedNumberFormatter formatter, ULocale locale) {
+ this.formatter = formatter;
+ this.locale = locale;
+ }
+
+ /**
+ * Formats a Number using the wrapped LocalizedNumberFormatter. The provided object must be a Number.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ if (!(obj instanceof Number)) {
+ throw new IllegalArgumentException();
+ }
+ FormattedNumber result = formatter.format((Number) obj);
+ // always return first occurrence:
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+ boolean found = result.nextFieldPosition(pos);
+ if (found && toAppendTo.length() != 0) {
+ pos.setBeginIndex(pos.getBeginIndex() + toAppendTo.length());
+ pos.setEndIndex(pos.getEndIndex() + toAppendTo.length());
+ }
+ result.appendTo(toAppendTo);
+ return toAppendTo;
+ }
+
+ /**
+ * Formats a Number using the wrapped LocalizedNumberFormatter. The provided object must be a Number.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
+ if (!(obj instanceof Number)) {
+ throw new IllegalArgumentException();
+ }
+ return formatter.format((Number) obj).toCharacterIterator();
+ }
+
+ /**
+ * Not supported. This method will throw UnsupportedOperationException.
+ */
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets the LocalizedNumberFormatter that this wrapper class uses to format numbers.
+ *
+ * @return The unwrapped LocalizedNumberFormatter.
+ */
+ public LocalizedNumberFormatter getNumberFormatter() {
+ return formatter;
+ }
+
+ @Override
+ public int hashCode() {
+ return formatter.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ if (!(other instanceof LocalizedNumberFormatterAsFormat)) {
+ return false;
+ }
+ return formatter.equals(((LocalizedNumberFormatterAsFormat) other).getNumberFormatter());
+ }
+
+ private Object writeReplace() throws ObjectStreamException {
+ Proxy proxy = new Proxy();
+ proxy.languageTag = locale.toLanguageTag();
+ proxy.skeleton = formatter.toSkeleton();
+ return proxy;
+ }
+
+ static class Proxy implements Externalizable {
+ String languageTag;
+ String skeleton;
+
+ // Must have public constructor, to enable Externalizable
+ public Proxy() {
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeByte(0); // version
+ out.writeUTF(languageTag);
+ out.writeUTF(skeleton);
+ }
+
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ in.readByte(); // version
+ languageTag = in.readUTF();
+ skeleton = in.readUTF();
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return NumberFormatter.fromSkeleton(skeleton)
+ .locale(ULocale.forLanguageTag(languageTag))
+ .toFormat();
+ }
+ }
+}
package com.ibm.icu.number;
import java.math.BigInteger;
+import java.text.Format;
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;
+import com.ibm.icu.impl.number.LocalizedNumberFormatterAsFormat;
import com.ibm.icu.impl.number.MacroProps;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.math.BigDecimal;
return withUnit.format(number);
}
+ /**
+ * Creates a representation of this LocalizedNumberFormat as a {@link java.text.Format}, enabling the
+ * use of this number formatter with APIs that need an object of that type, such as MessageFormat.
+ * <p>
+ * This API is not intended to be used other than for enabling API compatibility. The {@link #format}
+ * methods should normally be used when formatting numbers, not the Format object returned by this
+ * method.
+ *
+ * @return A Format wrapping this LocalizedNumberFormatter.
+ * @draft ICU 62
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberFormatter
+ */
+ public Format toFormat() {
+ return new LocalizedNumberFormatterAsFormat(this, resolve().loc);
+ }
+
/**
* This is the core entrypoint to the number formatting pipeline. It performs self-regulation: a
* static code path for the first few calls, and compiling a more efficient data structure if called
import com.ibm.icu.impl.PatternProps;
import com.ibm.icu.impl.Utility;
+import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.MessagePattern.ArgType;
import com.ibm.icu.text.MessagePattern.Part;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
case MODIFIER_INTEGER:
newFormat = NumberFormat.getIntegerInstance(ulocale);
break;
- default: // pattern
- newFormat = new DecimalFormat(style,
- new DecimalFormatSymbols(ulocale));
+ default: // pattern or skeleton
+ // Ignore leading whitespace when looking for "::", the skeleton signal sequence
+ int i = 0;
+ for (; PatternProps.isWhiteSpace(style.charAt(i)); i++);
+ if (style.regionMatches(i, "::", 0, 2)) {
+ // Skeleton
+ newFormat = NumberFormatter.fromSkeleton(style.substring(i + 2)).locale(ulocale).toFormat();
+ } else {
+ // Pattern
+ newFormat = new DecimalFormat(style, new DecimalFormatSymbols(ulocale));
+ }
break;
}
break;
format2 = serializeAndDeserialize(format1);
assertEquals("MessageFormats (empty pattern) before and after serialization are not equal", format1, format2);
- format1.applyPattern("ab{1}cd{0,number}ef{3,date}gh");
+ format1.applyPattern("ab{1}cd{0,number}ef{3,date}gh{4,number,::percent}ij");
format1.setFormat(2, null);
format1.setFormatByArgumentIndex(1, NumberFormat.getInstance(ULocale.ENGLISH));
format2 = serializeAndDeserialize(format1);
assertEquals(
"MessageFormat (with custom formats) does not "+
"format correctly after serialization",
- "ab3.3cd4,4ef***gh",
- format2.format(new Object[] { 4.4, 3.3, "+++", "***" }));
+ "ab3.3cd4,4ef***gh50\u00A0%ij",
+ format2.format(new Object[] { 4.4, 3.3, "+++", "***", 50 }));
}
}
int actualHashResult2 = testDF2.hashCode();
assertNotEquals("DateFormat hashCode() test: really the same hashcode?", actualHashResult1, actualHashResult2);
}
+
+ @Test
+ public void TestMessageFormatNumberSkeleton() {
+ Object[][] cases = new Object[][] {
+ { "{0,number,::percent}", ULocale.ENGLISH, 50, "50%" },
+ { "{0,number,::percent scale/100}", ULocale.ENGLISH, 0.5, "50%" },
+ { "{0,number, :: percent scale/100 }", ULocale.ENGLISH, 0.5, "50%" },
+ { "{0,number,::currency/USD}", ULocale.ENGLISH, 23, "$23.00" },
+ { "{0,number,::precision-integer}", ULocale.ENGLISH, 514.23, "514" },
+ { "{0,number,::.000}", ULocale.ENGLISH, 514.23, "514.230" },
+ { "{0,number,::.}", ULocale.ENGLISH, 514.23, "514" },
+ { "{0,number,::}", ULocale.FRENCH, 514.23, "514,23" },
+ { "Cost: {0,number,::currency/EUR}.", ULocale.ENGLISH, 4.3, "Cost: €4.30." },
+ { "{0,number,'::'0.00}", ULocale.ENGLISH, 50, "::50.00" }, // pattern literal
+ };
+
+ for (Object[] cas : cases) {
+ String messagePattern = (String) cas[0];
+ ULocale locale = (ULocale) cas[1];
+ Number arg = (Number) cas[2];
+ String expected = (String) cas[3];
+
+ MessageFormat msgf = new MessageFormat(messagePattern, locale);
+ StringBuffer sb = new StringBuffer();
+ FieldPosition fpos = new FieldPosition(0);
+ msgf.format(new Object[] { arg }, sb, fpos);
+
+ assertEquals(messagePattern, expected, sb.toString());
+ }
+ }
}
import java.math.RoundingMode;
import java.text.AttributedCharacterIterator;
import java.text.FieldPosition;
+import java.text.Format;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import org.junit.Ignore;
import org.junit.Test;
+import com.ibm.icu.dev.test.serializable.SerializableTestUtility;
import com.ibm.icu.impl.number.Grouper;
+import com.ibm.icu.impl.number.LocalizedNumberFormatterAsFormat;
import com.ibm.icu.impl.number.MacroProps;
import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.Padder.PadPosition;
assertFalse("No fraction part in an integer", fmtd.nextFieldPosition(actual));
}
+ /** Handler for serialization compatibility test suite. */
+ public static class FormatHandler implements SerializableTestUtility.Handler {
+ @Override
+ public Object[] getTestObjects() {
+ return new Object[] {
+ NumberFormatter.withLocale(ULocale.FRENCH).toFormat(),
+ NumberFormatter.fromSkeleton("percent").locale(ULocale.JAPANESE).toFormat(),
+ NumberFormatter.fromSkeleton("scientific .000").locale(ULocale.ENGLISH).toFormat() };
+ }
+
+ @Override
+ public boolean hasSameBehavior(Object a, Object b) {
+ LocalizedNumberFormatterAsFormat f1 = (LocalizedNumberFormatterAsFormat) a;
+ LocalizedNumberFormatterAsFormat f2 = (LocalizedNumberFormatterAsFormat) b;
+ String s1 = f1.format(514.23);
+ String s2 = f1.format(514.23);
+ String k1 = f1.getNumberFormatter().toSkeleton();
+ String k2 = f2.getNumberFormatter().toSkeleton();
+ return s1.equals(s2) && k1.equals(k2);
+ }
+ }
+
+ @Test
+ public void toFormat() {
+ LocalizedNumberFormatter lnf = NumberFormatter.withLocale(ULocale.FRENCH)
+ .precision(Precision.fixedFraction(3));
+ Format format = lnf.toFormat();
+ FieldPosition fpos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+ StringBuffer sb = new StringBuffer();
+ format.format(514.23, sb, fpos);
+ assertEquals("Should correctly format number", "514,230", sb.toString());
+ assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
+ assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
+ assertEquals("LocalizedNumberFormatter should round-trip",
+ lnf,
+ ((LocalizedNumberFormatterAsFormat) format).getNumberFormatter());
+ assertEquals("Should produce same character iterator",
+ lnf.format(514.23).toCharacterIterator().getAttributes(),
+ format.formatToCharacterIterator(514.23).getAttributes());
+ }
+
@Test
public void plurals() {
// TODO: Expand this test.
import com.ibm.icu.dev.test.format.MeasureUnitTest;
import com.ibm.icu.dev.test.format.PluralRulesTest;
+import com.ibm.icu.dev.test.number.NumberFormatterApiTest;
import com.ibm.icu.dev.test.number.PropertiesTest;
import com.ibm.icu.impl.JavaTimeZone;
import com.ibm.icu.impl.OlsonTimeZone;
map.put("com.ibm.icu.impl.number.DecimalFormatProperties", new PropertiesTest.PropertiesHandler());
map.put("com.ibm.icu.impl.number.CustomSymbolCurrency", new CurrencyHandler());
map.put("com.ibm.icu.number.SkeletonSyntaxException", new ExceptionHandler.SkeletonSyntaxExceptionHandler());
+ map.put("com.ibm.icu.impl.number.LocalizedNumberFormatterAsFormat", new NumberFormatterApiTest.FormatHandler());
map.put("com.ibm.icu.util.ICUException", new ICUExceptionHandler());
map.put("com.ibm.icu.util.ICUUncheckedIOException", new ICUUncheckedIOExceptionHandler());