double-conversion-cached-powers.o double-conversion-diy-fp.o double-conversion-fast-dtoa.o \
numparse_stringsegment.o numparse_unisets.o numparse_parsednumber.o \
numparse_impl.o numparse_symbols.o numparse_decimal.o numparse_scientific.o \
-numparse_currency.o numparse_affixes.o numparse_compositions.o \
+numparse_currency.o numparse_affixes.o numparse_compositions.o numparse_validators.o \
number_mapper.o number_multiplier.o number_currencysymbols.o
CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status)
- : fCurrency(currency), fLocaleName(locale.getName(), status) {}
+ : fCurrency(currency), fLocaleName(locale.getName(), status) {
+ fCurrencySymbol.setToBogus();
+ fIntlCurrencySymbol.setToBogus();
+}
+
+CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale,
+ const DecimalFormatSymbols& symbols, UErrorCode& status)
+ : CurrencySymbols(currency, locale, status) {
+ // If either of the overrides is present, save it in the local UnicodeString.
+ if (symbols.isCustomCurrencySymbol()) {
+ fCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kCurrencySymbol);
+ }
+ if (symbols.isCustomIntlCurrencySymbol()) {
+ fIntlCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol);
+ }
+}
+
+const char16_t* CurrencySymbols::getIsoCode() const {
+ return fCurrency.getISOCurrency();
+}
UnicodeString CurrencySymbols::getNarrowCurrencySymbol(UErrorCode& status) const {
+ // Note: currently no override is available for narrow currency symbol
return loadSymbol(UCURR_NARROW_SYMBOL_NAME, status);
}
+UnicodeString CurrencySymbols::getCurrencySymbol(UErrorCode& status) const {
+ if (!fCurrencySymbol.isBogus()) {
+ return fCurrencySymbol;
+ }
+ return loadSymbol(UCURR_SYMBOL_NAME, status);
+}
+
UnicodeString CurrencySymbols::loadSymbol(UCurrNameStyle selector, UErrorCode& status) const {
UBool ignoredIsChoiceFormatFillIn = FALSE;
int32_t symbolLen = 0;
return UnicodeString(TRUE, symbol, symbolLen);
}
+UnicodeString CurrencySymbols::getIntlCurrencySymbol(UErrorCode&) const {
+ if (!fIntlCurrencySymbol.isBogus()) {
+ return fIntlCurrencySymbol;
+ }
+ // Readonly-aliasing char16_t* constructor:
+ return UnicodeString(TRUE, fCurrency.getISOCurrency(), 3);
+}
+
UnicodeString CurrencySymbols::getPluralName(StandardPlural::Form plural, UErrorCode& status) const {
UBool isChoiceFormat = FALSE;
int32_t symbolLen = 0;
}
-CurrencyDataSymbols::CurrencyDataSymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status)
- : CurrencySymbols(currency, locale, status) {}
-
-UnicodeString CurrencyDataSymbols::getCurrencySymbol(UErrorCode& status) const {
- return loadSymbol(UCURR_SYMBOL_NAME, status);
-}
-
-UnicodeString CurrencyDataSymbols::getIntlCurrencySymbol(UErrorCode&) const {
- // Readonly-aliasing char16_t* constructor:
- return UnicodeString(TRUE, fCurrency.getISOCurrency(), 3);
-}
-
-
-CurrencyCustomSymbols::CurrencyCustomSymbols(CurrencyUnit currency, const Locale& locale,
- const DecimalFormatSymbols& symbols, UErrorCode& status)
- : CurrencySymbols(currency, locale, status) {
- // Hit the data bundle if the DecimalFormatSymbols version is not custom.
- // Note: the CurrencyDataSymbols implementation waits to hit the data bundle until requested.
- if (symbols.isCustomCurrencySymbol()) {
- fCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kCurrencySymbol);
- } else {
- fCurrencySymbol = loadSymbol(UCURR_SYMBOL_NAME, status);
- }
- if (symbols.isCustomIntlCurrencySymbol()) {
- fIntlCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol);
- } else {
- // UnicodeString copy constructor since we don't know about the lifetime of the CurrencyUnit
- fIntlCurrencySymbol = UnicodeString(currency.getISOCurrency(), 3);
- }
-}
-
-UnicodeString CurrencyCustomSymbols::getCurrencySymbol(UErrorCode&) const {
- return fCurrencySymbol;
-}
-
-UnicodeString CurrencyCustomSymbols::getIntlCurrencySymbol(UErrorCode&) const {
- return fIntlCurrencySymbol;
-}
-
-
CurrencyUnit
icu::number::impl::resolveCurrency(const DecimalFormatProperties& properties, const Locale& locale,
UErrorCode& status) {
namespace impl {
-class CurrencySymbols {
+class CurrencySymbols : public UMemory {
public:
CurrencySymbols() = default; // default constructor: leaves class in valid but undefined state
- explicit CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status);
+ /** Creates an instance in which all symbols are loaded from data. */
+ CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status);
- virtual UnicodeString getCurrencySymbol(UErrorCode& status) const = 0;
+ /** Creates an instance in which some symbols might be pre-populated. */
+ CurrencySymbols(CurrencyUnit currency, const Locale& locale, const DecimalFormatSymbols& symbols,
+ UErrorCode& status);
- virtual UnicodeString getIntlCurrencySymbol(UErrorCode& status) const = 0;
+ const char16_t* getIsoCode() const;
- // Put narrow and plural symbols in the base class since there is no API for overriding them
UnicodeString getNarrowCurrencySymbol(UErrorCode& status) const;
+ UnicodeString getCurrencySymbol(UErrorCode& status) const;
+
+ UnicodeString getIntlCurrencySymbol(UErrorCode& status) const;
+
UnicodeString getPluralName(StandardPlural::Form plural, UErrorCode& status) const;
protected:
+ // Required fields:
CurrencyUnit fCurrency;
CharString fLocaleName;
- UnicodeString loadSymbol(UCurrNameStyle selector, UErrorCode& status) const;
-};
-
-
-class CurrencyDataSymbols : public CurrencySymbols, public UMemory {
- public:
- CurrencyDataSymbols() = default; // default constructor: leaves class in valid but undefined state
-
- CurrencyDataSymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status);
-
- UnicodeString getCurrencySymbol(UErrorCode& status) const U_OVERRIDE;
-
- UnicodeString getIntlCurrencySymbol(UErrorCode& status) const U_OVERRIDE;
-};
-
-
-class CurrencyCustomSymbols : public CurrencySymbols, public UMemory {
- public:
- CurrencyCustomSymbols() = default; // default constructor: leaves class in valid but undefined state
-
- CurrencyCustomSymbols(CurrencyUnit currency, const Locale& locale, const DecimalFormatSymbols& symbols,
- UErrorCode& status);
-
- UnicodeString getCurrencySymbol(UErrorCode& status) const U_OVERRIDE;
-
- UnicodeString getIntlCurrencySymbol(UErrorCode& status) const U_OVERRIDE;
-
- private:
+ // Optional fields:
UnicodeString fCurrencySymbol;
UnicodeString fIntlCurrencySymbol;
+
+ UnicodeString loadSymbol(UCurrNameStyle selector, UErrorCode& status) const;
};
padString.setToBogus();
parseCaseSensitive = false;
parseIntegerOnly = false;
- parseLenient = false;
+ parseMode.nullify();
parseNoExponent = false;
parseToBigDecimal = false;
parseAllInput = UNUM_MAYBE;
eq = eq && padString == other.padString;
eq = eq && parseCaseSensitive == other.parseCaseSensitive;
eq = eq && parseIntegerOnly == other.parseIntegerOnly;
- eq = eq && parseLenient == other.parseLenient;
+ eq = eq && parseMode == other.parseMode;
eq = eq && parseNoExponent == other.parseNoExponent;
eq = eq && parseToBigDecimal == other.parseToBigDecimal;
eq = eq && parseAllInput == other.parseAllInput;
}
};
+/** Controls the set of rules for parsing a string from the old DecimalFormat API. */
+enum ParseMode {
+ /**
+ * Lenient mode should be used if you want to accept malformed user input. It will use heuristics
+ * to attempt to parse through typographical errors in the string.
+ */
+ PARSE_MODE_LENIENT,
+
+ /**
+ * Strict mode should be used if you want to require that the input is well-formed. More
+ * specifically, it differs from lenient mode in the following ways:
+ *
+ * <ul>
+ * <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the
+ * grouping width is 3, as in the pattern "#,##0".
+ * <li>The string must contain a complete prefix and suffix. For example, if the pattern is
+ * "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail.
+ * (The latter strings would be accepted in lenient mode.)
+ * <li>Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace
+ * is allowed to occur arbitrarily before and after prefixes and exponent separators.
+ * <li>Leading grouping separators are not allowed, as in ",123".
+ * <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus
+ * or minus sign can always precede a number.
+ * <li>The set of characters that can be interpreted as a decimal or grouping separator is
+ * smaller.
+ * <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
+ * specified in either the current pattern string or in a valid pattern string for the current
+ * locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would
+ * fail to match.
+ * </ul>
+ */
+ PARSE_MODE_STRICT,
+};
+
// Exported as U_I18N_API because it is needed for the unit test PatternStringTest
struct U_I18N_API DecimalFormatProperties {
UnicodeString padString;
bool parseCaseSensitive;
bool parseIntegerOnly;
- bool parseLenient;
+ NullableValue<ParseMode> parseMode;
bool parseNoExponent;
bool parseToBigDecimal;
UNumberFormatAttributeValue parseAllInput; // ICU4C-only
// Used by the DecimalFormat code path
currencySymbols = macros.currencySymbols;
} else {
- fWarehouse.fCurrencyDataSymbols = {currency, macros.locale, status};
- currencySymbols = &fWarehouse.fCurrencyDataSymbols;
+ fWarehouse.fCurrencySymbols = {currency, macros.locale, status};
+ currencySymbols = &fWarehouse.fCurrencySymbols;
}
UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
// Value objects possibly used by the number formatting pipeline:
struct Warehouse {
- CurrencyDataSymbols fCurrencyDataSymbols;
+ CurrencySymbols fCurrencySymbols;
} fWarehouse;
// NOTE: Slicing is OK.
macros.unit = currency; // NOLINT
}
- if (symbols.isCustomCurrencySymbol() || symbols.isCustomIntlCurrencySymbol()) {
- warehouse.currencyCustomSymbols = {currency, locale, symbols, status};
- macros.currencySymbols = &warehouse.currencyCustomSymbols;
- }
+ warehouse.currencySymbols = {currency, locale, symbols, status};
+ macros.currencySymbols = &warehouse.currencySymbols;
///////////////////////
// ROUNDING STRATEGY //
void setTo(const DecimalFormatProperties& properties, UErrorCode& status);
+ PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state
+
+ PropertiesAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) {
+ setTo(properties, status);
+ }
+
// AffixPatternProvider Methods:
char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE;
struct DecimalFormatWarehouse {
PropertiesAffixPatternProvider propertiesAPP;
CurrencyPluralInfoAffixProvider currencyPluralInfoAPP;
- CurrencyCustomSymbols currencyCustomSymbols;
+ CurrencySymbols currencySymbols;
};
}
NumberParseMatcher& AffixTokenMatcherWarehouse::currency(UErrorCode& status) {
- return fCurrency = {{fSetupData->locale, status},
- {fSetupData->currencyCode, fSetupData->currency1, fSetupData->currency2}};
+ return fCurrency = {{fSetupData->locale, status}, {fSetupData->currencySymbols, status}};
}
IgnorablesMatcher& AffixTokenMatcherWarehouse::ignorables() {
#include "numparse_symbols.h"
#include "numparse_currency.h"
#include "number_affixutils.h"
+#include "number_currencysymbols.h"
#include <array>
using ::icu::number::impl::AffixPatternProvider;
using ::icu::number::impl::TokenConsumer;
+using ::icu::number::impl::CurrencySymbols;
class CodePointMatcher : public NumberParseMatcher, public UMemory {
struct AffixTokenMatcherSetupData {
- const UChar* currencyCode;
- const UnicodeString& currency1;
- const UnicodeString& currency2;
+ const CurrencySymbols& currencySymbols;
const DecimalFormatSymbols& dfs;
IgnorablesMatcher& ignorables;
const Locale& locale;
}
-CurrencyCustomMatcher::CurrencyCustomMatcher(const char16_t* currencyCode, const UnicodeString& currency1,
- const UnicodeString& currency2)
- : fCurrency1(currency1), fCurrency2(currency2) {
- utils::copyCurrencyCode(fCurrencyCode, currencyCode);
+CurrencyCustomMatcher::CurrencyCustomMatcher(const CurrencySymbols& currencySymbols, UErrorCode& status)
+ : fCurrency1(currencySymbols.getCurrencySymbol(status)),
+ fCurrency2(currencySymbols.getIntlCurrencySymbol(status)) {
+ utils::copyCurrencyCode(fCurrencyCode, currencySymbols.getIsoCode());
}
bool CurrencyCustomMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode&) const {
#include "numparse_types.h"
#include "numparse_compositions.h"
#include "charstr.h"
+#include "number_currencysymbols.h"
U_NAMESPACE_BEGIN namespace numparse {
namespace impl {
+using ::icu::number::impl::CurrencySymbols;
/**
* Matches currencies according to all available strings in locale data.
public:
CurrencyCustomMatcher() = default; // WARNING: Leaves the object in an unusable state
- CurrencyCustomMatcher(const char16_t* currencyCode, const UnicodeString& currency1,
- const UnicodeString& currency2);
+ CurrencyCustomMatcher(const CurrencySymbols& currencySymbols, UErrorCode& status);
bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override;
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
+#include <typeinfo>
+#include <array>
+#include <iostream>
#include "number_types.h"
#include "number_patternstring.h"
#include "numparse_types.h"
#include "numparse_symbols.h"
#include "numparse_decimal.h"
#include "unicode/numberformatter.h"
-
-#include <typeinfo>
-#include <array>
-#include <iostream>
#include "cstr.h"
+#include "number_mapper.h"
+#include "numparse_unisets.h"
using namespace icu;
using namespace icu::number;
NumberParserImpl::createSimpleParser(const Locale& locale, const UnicodeString& patternString,
parse_flags_t parseFlags, UErrorCode& status) {
- auto* parser = new NumberParserImpl(parseFlags, true);
+ LocalPointer<NumberParserImpl> parser(new NumberParserImpl(parseFlags, true));
DecimalFormatSymbols symbols(locale, status);
parser->fLocalMatchers.ignorables = {unisets::DEFAULT_IGNORABLES};
IgnorablesMatcher& ignorables = parser->fLocalMatchers.ignorables;
- const UChar currencyCode[] = u"USD";
- UnicodeString currency1(u"IU$");
- UnicodeString currency2(u"ICU");
+ DecimalFormatSymbols dfs(locale, status);
+ dfs.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"IU$", status);
+ dfs.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, u"ICU", status);
+ CurrencySymbols currencySymbols({u"ICU", status}, locale, dfs, status);
ParsedPatternInfo patternInfo;
PatternParser::parseToPatternInfo(patternString, patternInfo, status);
// The following statements set up the affix matchers.
AffixTokenMatcherSetupData affixSetupData = {
- currencyCode, currency1, currency2, symbols, ignorables, locale};
+ currencySymbols, symbols, ignorables, locale};
parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData};
parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse};
parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers(
// parser.addMatcher(new RequireNumberMatcher());
parser->freeze();
- return parser;
+ return parser.orphan();
}
-//NumberParserImpl*
-//NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatProperties& properties,
-// DecimalFormatSymbols symbols, bool parseCurrency,
-// bool optimize, UErrorCode& status) {
-// // TODO
-// status = U_UNSUPPORTED_ERROR;
-// return nullptr;
-//}
+NumberParserImpl*
+NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatProperties& properties,
+ const DecimalFormatSymbols& symbols, bool parseCurrency,
+ bool computeLeads, UErrorCode& status) {
+ Locale locale = symbols.getLocale();
+ PropertiesAffixPatternProvider patternInfo(properties, status);
+ CurrencyUnit currency = resolveCurrency(properties, locale, status);
+ CurrencySymbols currencySymbols(currency, locale, symbols, status);
+ bool isStrict = properties.parseMode.getOrDefault(PARSE_MODE_STRICT) == PARSE_MODE_STRICT;
+ Grouper grouper = Grouper::forProperties(properties);
+ int parseFlags = 0;
+ // Fraction grouping is disabled by default because it has never been supported in DecimalFormat
+ parseFlags |= PARSE_FLAG_FRACTION_GROUPING_DISABLED;
+ if (!properties.parseCaseSensitive) {
+ parseFlags |= PARSE_FLAG_IGNORE_CASE;
+ }
+ if (properties.parseIntegerOnly) {
+ parseFlags |= PARSE_FLAG_INTEGER_ONLY;
+ }
+ if (properties.signAlwaysShown) {
+ parseFlags |= PARSE_FLAG_PLUS_SIGN_ALLOWED;
+ }
+ if (isStrict) {
+ parseFlags |= PARSE_FLAG_STRICT_GROUPING_SIZE;
+ parseFlags |= PARSE_FLAG_STRICT_SEPARATORS;
+ parseFlags |= PARSE_FLAG_USE_FULL_AFFIXES;
+ parseFlags |= PARSE_FLAG_EXACT_AFFIX;
+ } else {
+ parseFlags |= PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES;
+ }
+ if (grouper.getPrimary() <= 0) {
+ parseFlags |= PARSE_FLAG_GROUPING_DISABLED;
+ }
+ if (parseCurrency || patternInfo.hasCurrencySign()) {
+ parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS;
+ }
+ if (computeLeads) {
+ parseFlags |= PARSE_FLAG_OPTIMIZE;
+ }
+ IgnorablesMatcher ignorables(isStrict ? unisets::DEFAULT_IGNORABLES : unisets::STRICT_IGNORABLES);
+
+ LocalPointer<NumberParserImpl> parser(new NumberParserImpl(parseFlags, status));
+
+ //////////////////////
+ /// AFFIX MATCHERS ///
+ //////////////////////
+
+ // The following statements set up the affix matchers.
+ AffixTokenMatcherSetupData affixSetupData = {
+ currencySymbols,
+ symbols,
+ ignorables,
+ locale};
+ parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData};
+ parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse};
+ parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers(
+ patternInfo, *parser, ignorables, parseFlags, status);
+
+ ////////////////////////
+ /// CURRENCY MATCHER ///
+ ////////////////////////
+
+ if (parseCurrency || patternInfo.hasCurrencySign()) {
+ parser->addMatcher(parser->fLocalMatchers.currencyCustom = {currencySymbols, status});
+ parser->addMatcher(parser->fLocalMatchers.currencyNames = {locale, status});
+ }
+
+ ///////////////////////////////
+ /// OTHER STANDARD MATCHERS ///
+ ///////////////////////////////
+
+ if (!isStrict) {
+ parser->addMatcher(parser->fLocalMatchers.plusSign = {symbols, false});
+ parser->addMatcher(parser->fLocalMatchers.minusSign = {symbols, false});
+ parser->addMatcher(parser->fLocalMatchers.nan = {symbols});
+ parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
+ parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
+ }
+ parser->addMatcher(parser->fLocalMatchers.infinity = {symbols});
+ UnicodeString padString = properties.padString;
+ if (!padString.isBogus() && !ignorables.getSet()->contains(padString)) {
+ parser->addMatcher(parser->fLocalMatchers.padding = {padString});
+ }
+ parser->addMatcher(parser->fLocalMatchers.ignorables);
+ parser->addMatcher(parser->fLocalMatchers.decimal = {symbols, grouper, parseFlags});
+ if (!properties.parseNoExponent) {
+ parser->addMatcher(parser->fLocalMatchers.scientific = {symbols, grouper});
+ }
+
+ //////////////////
+ /// VALIDATORS ///
+ //////////////////
+
+ parser->addMatcher(parser->fLocalValidators.number = {});
+ if (isStrict) {
+ parser->addMatcher(parser->fLocalValidators.affix = {});
+ }
+ if (isStrict && properties.minimumExponentDigits > 0) {
+ parser->addMatcher(parser->fLocalValidators.exponent = {});
+ }
+ if (parseCurrency) {
+ parser->addMatcher(parser->fLocalValidators.currency = {});
+ }
+ if (properties.decimalPatternMatchRequired) {
+ bool patternHasDecimalSeparator =
+ properties.decimalSeparatorAlwaysShown || properties.maximumFractionDigits != 0;
+ parser->addMatcher(parser->fLocalValidators.decimalSeparator = {patternHasDecimalSeparator});
+ }
+
+ // TODO: MULTIPLIER
+// if (properties.getMultiplier() != null) {
+// // We need to use a math context in order to prevent non-terminating decimal expansions.
+// // This is only used when dividing by the multiplier.
+// parser.addMatcher(new MultiplierHandler(properties.getMultiplier(),
+// RoundingUtils.getMathContextOr34Digits(properties)));
+// }
+
+ parser->freeze();
+ return parser.orphan();
+}
NumberParserImpl::NumberParserImpl(parse_flags_t parseFlags, bool computeLeads)
: fParseFlags(parseFlags), fComputeLeads(computeLeads) {
#include "numparse_currency.h"
#include "numparse_affixes.h"
#include "number_decimfmtprops.h"
+#include "unicode/localpointer.h"
+#include "numparse_validators.h"
U_NAMESPACE_BEGIN namespace numparse {
namespace impl {
parse_flags_t parseFlags, UErrorCode& status);
static NumberParserImpl* createParserFromProperties(
- const number::impl::DecimalFormatProperties& properties, DecimalFormatSymbols symbols,
+ const number::impl::DecimalFormatProperties& properties, const DecimalFormatSymbols& symbols,
bool parseCurrency, bool optimize, UErrorCode& status);
void addMatcher(NumberParseMatcher& matcher) override;
DecimalMatcher decimal;
ScientificMatcher scientific;
CurrencyNamesMatcher currencyNames;
+ CurrencyCustomMatcher currencyCustom;
AffixMatcherWarehouse affixMatcherWarehouse;
AffixTokenMatcherWarehouse affixTokenMatcherWarehouse;
} fLocalMatchers;
+ struct {
+ RequireAffixValidator affix;
+ RequireCurrencyValidator currency;
+ RequireDecimalSeparatorValidator decimalSeparator;
+ RequireExponentValidator exponent;
+ RequireNumberValidator number;
+ } fLocalValidators;
NumberParserImpl(parse_flags_t parseFlags, bool computeLeads);
--- /dev/null
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include "numparse_types.h"
+#include "numparse_validators.h"
+#include "numparse_unisets.h"
+
+using namespace icu;
+using namespace icu::numparse;
+using namespace icu::numparse::impl;
+
+
+void RequireAffixValidator::postProcess(ParsedNumber& result) const {
+ if (result.prefix.isBogus() || result.suffix.isBogus()) {
+ // We saw a prefix or a suffix but not both. Fail the parse.
+ result.flags |= FLAG_FAIL;
+ }
+}
+
+UnicodeString RequireAffixValidator::toString() const {
+ return u"<ReqAffix>";
+}
+
+
+void RequireCurrencyValidator::postProcess(ParsedNumber& result) const {
+ if (result.currencyCode[0] == 0) {
+ result.flags |= FLAG_FAIL;
+ }
+}
+
+UnicodeString RequireCurrencyValidator::toString() const {
+ return u"<ReqCurrency>";
+}
+
+
+RequireDecimalSeparatorValidator::RequireDecimalSeparatorValidator(bool patternHasDecimalSeparator)
+ : fPatternHasDecimalSeparator(patternHasDecimalSeparator) {
+}
+
+void RequireDecimalSeparatorValidator::postProcess(ParsedNumber& result) const {
+ bool parseHasDecimalSeparator = 0 != (result.flags & FLAG_HAS_DECIMAL_SEPARATOR);
+ if (parseHasDecimalSeparator != fPatternHasDecimalSeparator) {
+ result.flags |= FLAG_FAIL;
+ }
+}
+
+UnicodeString RequireDecimalSeparatorValidator::toString() const {
+ return u"<ReqDecimal>";
+}
+
+
+void RequireExponentValidator::postProcess(ParsedNumber& result) const {
+ if (0 == (result.flags & FLAG_HAS_EXPONENT)) {
+ result.flags |= FLAG_FAIL;
+ }
+}
+
+UnicodeString RequireExponentValidator::toString() const {
+ return u"<ReqExponent>";
+}
+
+
+void RequireNumberValidator::postProcess(ParsedNumber& result) const {
+ // Require that a number is matched.
+ if (!result.seenNumber()) {
+ result.flags |= FLAG_FAIL;
+ }
+}
+
+UnicodeString RequireNumberValidator::toString() const {
+ return u"<ReqNumber>";
+}
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
--- /dev/null
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
+#ifndef __SOURCE_NUMPARSE_VALIDATORS_H__
+#define __SOURCE_NUMPARSE_VALIDATORS_H__
+
+#include "numparse_types.h"
+#include "numparse_unisets.h"
+
+U_NAMESPACE_BEGIN namespace numparse {
+namespace impl {
+
+
+class ValidationMatcher : public NumberParseMatcher {
+ public:
+ bool match(StringSegment&, ParsedNumber&, UErrorCode&) const U_OVERRIDE {
+ // No-op
+ return false;
+ }
+
+ const UnicodeSet& getLeadCodePoints() U_OVERRIDE {
+ // No-op
+ return *unisets::get(unisets::EMPTY);
+ }
+
+ virtual void postProcess(ParsedNumber& result) const U_OVERRIDE = 0;
+};
+
+
+class RequireAffixValidator : public ValidationMatcher, public UMemory {
+ public:
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+};
+
+
+class RequireCurrencyValidator : public ValidationMatcher, public UMemory {
+ public:
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+};
+
+
+class RequireDecimalSeparatorValidator : public ValidationMatcher, public UMemory {
+ public:
+ RequireDecimalSeparatorValidator() = default; // leaves instance in valid but undefined state
+
+ RequireDecimalSeparatorValidator(bool patternHasDecimalSeparator);
+
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+
+ private:
+ bool fPatternHasDecimalSeparator;
+};
+
+
+class RequireExponentValidator : public ValidationMatcher, public UMemory {
+ public:
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+};
+
+
+class RequireNumberValidator : public ValidationMatcher, public UMemory {
+ public:
+ void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+ UnicodeString toString() const U_OVERRIDE;
+};
+
+
+} // namespace impl
+} // namespace numparse
+U_NAMESPACE_END
+
+#endif //__SOURCE_NUMPARSE_VALIDATORS_H__
+#endif /* #if !UCONFIG_NO_FORMATTING */
IcuTestErrorCode status(*this, "testCurrencyAnyMatcher");
IgnorablesMatcher ignorables(unisets::DEFAULT_IGNORABLES);
+ Locale locale = Locale::getEnglish();
+
+ DecimalFormatSymbols dfs(locale, status);
+ dfs.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"IU$", status);
+ dfs.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, u"ICU", status);
+ CurrencySymbols currencySymbols({u"ICU", status}, locale, dfs, status);
+
AffixTokenMatcherSetupData affixSetupData = {
- u"ICU",
- u"IU$",
- u"ICU",
- {"en", status},
- ignorables,
- "en"};
+ currencySymbols, {"en", status}, ignorables, "en"};
AffixTokenMatcherWarehouse warehouse(&affixSetupData);
NumberParseMatcher& matcher = warehouse.currency(status);
void NumberParserTest::testAffixPatternMatcher() {
IcuTestErrorCode status(*this, "testAffixPatternMatcher");
+ Locale locale = Locale::getEnglish();
IgnorablesMatcher ignorables(unisets::DEFAULT_IGNORABLES);
+
+ DecimalFormatSymbols dfs(locale, status);
+ dfs.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"IU$", status);
+ dfs.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, u"ICU", status);
+ CurrencySymbols currencySymbols({u"ICU", status}, locale, dfs, status);
+
AffixTokenMatcherSetupData affixSetupData = {
- u"USD",
- u"foo",
- u"bar",
- {"en", status},
- ignorables,
- "en"};
+ currencySymbols, {"en", status}, ignorables, "en"};
AffixTokenMatcherWarehouse warehouse(&affixSetupData);
static const struct TestCase {
mod.setPatternInfo(&patternInfo);
mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
DecimalFormatSymbols symbols(Locale::getEnglish(), status);
- CurrencyDataSymbols currencySymbols({u"USD", status}, "en", status);
+ CurrencySymbols currencySymbols({u"USD", status}, "en", status);
assertSuccess("Spot 2", status);
mod.setSymbols(&symbols, ¤cySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
mod.setPatternInfo(&patternInfo);
mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
DecimalFormatSymbols symbols(Locale::getEnglish(), status);
- CurrencyDataSymbols currencySymbols({u"USD", status}, "en", status);
+ CurrencySymbols currencySymbols({u"USD", status}, "en", status);
assertSuccess("Spot 2", status);
mod.setSymbols(&symbols, ¤cySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
mod.setNumberProperties(1, StandardPlural::Form::COUNT);
mod.setPatternInfo(&patternInfo);
mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
DecimalFormatSymbols symbols(Locale::getEnglish(), status);
- CurrencyDataSymbols currencySymbols({u"USD", status}, "en", status);
+ CurrencySymbols currencySymbols({u"USD", status}, "en", status);
assertSuccess("Spot 2", status);
if (U_FAILURE(status)) { return; }
mod.setSymbols(&symbols, ¤cySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);