U_DRAFT double U_EXPORT2 ufmt_getDouble(UFormattable *fmt, UErrorCode *status) { Formattable *obj = Formattable::fromUFormattable(fmt); return obj->getDouble(*status); }
U_DRAFT int32_t U_EXPORT2 ufmt_getLong(UFormattable *fmt, UErrorCode *status) { Formattable *obj = Formattable::fromUFormattable(fmt); return obj->getLong(*status); }
void TimeUnitFormat::parseObject(const UnicodeString& source, Formattable& result, ParsePosition& pos) const { Formattable resultNumber(0.0); UBool withNumberFormat = false; TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT; int32_t oldPos = pos.getIndex(); int32_t newPos = -1; int32_t longestParseDistance = 0; UnicodeString* countOfLongestMatch = NULL; #ifdef TMUTFMT_DEBUG char res[1000]; source.extract(0, source.length(), res, "UTF-8"); std::cout << "parse source: " << res << "\n"; #endif // parse by iterating through all available patterns // and looking for the longest match. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; int32_t elemPos = UHASH_FIRST; const UHashElement* elem = NULL; while ((elem = countToPatterns->nextElement(elemPos)) != NULL){ const UHashTok keyTok = elem->key; UnicodeString* count = (UnicodeString*)keyTok.pointer; #ifdef TMUTFMT_DEBUG count->extract(0, count->length(), res, "UTF-8"); std::cout << "parse plural count: " << res << "\n"; #endif const UHashTok valueTok = elem->value; // the value is a pair of MessageFormat* MessageFormat** patterns = (MessageFormat**)valueTok.pointer; for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT; style = (UTimeUnitFormatStyle)(style + 1)) { MessageFormat* pattern = patterns[style]; pos.setErrorIndex(-1); pos.setIndex(oldPos); // see if we can parse Formattable parsed; pattern->parseObject(source, parsed, pos); if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) { continue; } #ifdef TMUTFMT_DEBUG std::cout << "parsed.getType: " << parsed.getType() << "\n"; #endif Formattable tmpNumber(0.0); if (pattern->getArgTypeCount() != 0) { Formattable& temp = parsed[0]; if (temp.getType() == Formattable::kString) { UnicodeString tmpString; UErrorCode pStatus = U_ZERO_ERROR; getNumberFormat().parse(temp.getString(tmpString), tmpNumber, pStatus); if (U_FAILURE(pStatus)) { continue; } } else if (temp.isNumeric()) { tmpNumber = temp; } else { continue; } } int32_t parseDistance = pos.getIndex() - oldPos; if (parseDistance > longestParseDistance) { if (pattern->getArgTypeCount() != 0) { resultNumber = tmpNumber; withNumberFormat = true; } else { withNumberFormat = false; } resultTimeUnit = i; newPos = pos.getIndex(); longestParseDistance = parseDistance; countOfLongestMatch = count; } } } } /* After find the longest match, parse the number. * Result number could be null for the pattern without number pattern. * such as unit pattern in Arabic. * When result number is null, use plural rule to set the number. */ if (withNumberFormat == false && longestParseDistance != 0) { // set the number using plurrual count if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) { resultNumber = Formattable(0.0); } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) { resultNumber = Formattable(1.0); } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) { resultNumber = Formattable(2.0); } else { // should not happen. // TODO: how to handle? resultNumber = Formattable(3.0); } } if (longestParseDistance == 0) { pos.setIndex(oldPos); pos.setErrorIndex(0); } else { UErrorCode status = U_ZERO_ERROR; TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status); if (U_SUCCESS(status)) { result.adoptObject(tmutamt); pos.setIndex(newPos); pos.setErrorIndex(-1); } else { pos.setIndex(oldPos); pos.setErrorIndex(0); } } }
void NumberFormatRoundTripTest::test(NumberFormat *fmt, const Formattable& value) { fmt->setMaximumFractionDigits(999); DecimalFormat *df = dynamic_cast<DecimalFormat *>(fmt); if(df != NULL) { df->setRoundingIncrement(0.0); } UErrorCode status = U_ZERO_ERROR; UnicodeString s, s2, temp; if(isDouble(value)) s = fmt->format(value.getDouble(), s); else s = fmt->format(value.getLong(), s); Formattable n; UBool show = verbose; if(DEBUG_VAR) logln(/*value.getString(temp) +*/ " F> " + escape(s)); fmt->parse(s, n, status); failure(status, "fmt->parse"); if(DEBUG_VAR) logln(escape(s) + " P> " /*+ n.getString(temp)*/); if(isDouble(n)) s2 = fmt->format(n.getDouble(), s2); else s2 = fmt->format(n.getLong(), s2); if(DEBUG_VAR) logln(/*n.getString(temp) +*/ " F> " + escape(s2)); if(STRING_COMPARE) { if (s != s2) { errln("*** STRING ERROR \"" + escape(s) + "\" != \"" + escape(s2) + "\""); show = TRUE; } } if(EXACT_NUMERIC_COMPARE) { if(value != n) { errln("*** NUMERIC ERROR"); show = TRUE; } } else { // Compute proportional error double error = proportionalError(value, n); if(error > MAX_ERROR) { errln(UnicodeString("*** NUMERIC ERROR ") + error); show = TRUE; } if (error > max_numeric_error) max_numeric_error = error; if (error < min_numeric_error) min_numeric_error = error; } if (show) { errln(/*value.getString(temp) +*/ typeOf(value, temp) + " F> " + escape(s) + " P> " + (n.getType() == Formattable::kDouble ? n.getDouble() : (double)n.getLong()) /*n.getString(temp) */ + typeOf(n, temp) + " F> " + escape(s2)); } }
/** * Test basic */ void TimeUnitTest::testBasic() { const char* locales[] = {"en", "sl", "fr", "zh", "ar", "ru", "zh_Hant", "pa"}; for ( unsigned int locIndex = 0; locIndex < UPRV_LENGTHOF(locales); ++locIndex ) { UErrorCode status = U_ZERO_ERROR; Locale loc(locales[locIndex]); TimeUnitFormat** formats = new TimeUnitFormat*[2]; formats[UTMUTFMT_FULL_STYLE] = new TimeUnitFormat(loc, status); if (!assertSuccess("TimeUnitFormat(full)", status, TRUE)) return; formats[UTMUTFMT_ABBREVIATED_STYLE] = new TimeUnitFormat(loc, UTMUTFMT_ABBREVIATED_STYLE, status); if (!assertSuccess("TimeUnitFormat(short)", status)) return; #ifdef TUFMTTS_DEBUG std::cout << "locale: " << locales[locIndex] << "\n"; #endif for (int style = UTMUTFMT_FULL_STYLE; style <= UTMUTFMT_ABBREVIATED_STYLE; ++style) { for (TimeUnit::UTimeUnitFields j = TimeUnit::UTIMEUNIT_YEAR; j < TimeUnit::UTIMEUNIT_FIELD_COUNT; j = (TimeUnit::UTimeUnitFields)(j+1)) { #ifdef TUFMTTS_DEBUG std::cout << "time unit: " << j << "\n"; #endif double tests[] = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 5, 10, 100, 101.35}; for (unsigned int i = 0; i < UPRV_LENGTHOF(tests); ++i) { #ifdef TUFMTTS_DEBUG std::cout << "number: " << tests[i] << "\n"; #endif TimeUnitAmount* source = new TimeUnitAmount(tests[i], j, status); if (!assertSuccess("TimeUnitAmount()", status)) return; UnicodeString formatted; Formattable formattable; formattable.adoptObject(source); formatted = ((Format*)formats[style])->format(formattable, formatted, status); if (!assertSuccess("format()", status)) return; #ifdef TUFMTTS_DEBUG char formatResult[1000]; formatted.extract(0, formatted.length(), formatResult, "UTF-8"); std::cout << "format result: " << formatResult << "\n"; #endif Formattable result; ((Format*)formats[style])->parseObject(formatted, result, status); if (!assertSuccess("parseObject()", status)) return; if (!tmaEqual(*((TimeUnitAmount *)result.getObject()), *((TimeUnitAmount *) formattable.getObject()))) { dataerrln("No round trip: "); } // other style parsing Formattable result_1; ((Format*)formats[1-style])->parseObject(formatted, result_1, status); if (!assertSuccess("parseObject()", status)) return; if (!tmaEqual(*((TimeUnitAmount *)result_1.getObject()), *((TimeUnitAmount *) formattable.getObject()))) { dataerrln("No round trip: "); } } } } delete formats[UTMUTFMT_FULL_STYLE]; delete formats[UTMUTFMT_ABBREVIATED_STYLE]; delete[] formats; } }
/* static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring text, jobject position, jboolean parseBigDecimal) { */ JNIEXPORT jobject JNICALL Java_com_ibm_icu4jni_text_NativeDecimalFormat_parse(JNIEnv* env, jclass, jint addr, jstring text, jobject position, jboolean parseBigDecimal) { jclass parsePositionClass = env->FindClass("java/text/ParsePosition"); static jmethodID gPP_getIndex = env->GetMethodID(parsePositionClass, "getIndex", "()I"); static jmethodID gPP_setIndex = env->GetMethodID(parsePositionClass, "setIndex", "(I)V"); static jmethodID gPP_setErrorIndex = env->GetMethodID(parsePositionClass, "setErrorIndex", "(I)V"); // make sure the ParsePosition is valid. Actually icu4c would parse a number // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too int parsePos = env->CallIntMethod(position, gPP_getIndex, NULL); if (parsePos < 0 || parsePos > env->GetStringLength(text)) { return NULL; } Formattable res; ParsePosition pp(parsePos); ScopedJavaUnicodeString src(env, text); DecimalFormat* fmt = toDecimalFormat(addr); fmt->parse(src.unicodeString(), res, pp); if (pp.getErrorIndex() == -1) { env->CallVoidMethod(position, gPP_setIndex, (jint) pp.getIndex()); } else { env->CallVoidMethod(position, gPP_setErrorIndex, (jint) pp.getErrorIndex()); return NULL; } if (parseBigDecimal) { UErrorCode status = U_ZERO_ERROR; StringPiece str = res.getDecimalNumber(status); if (U_SUCCESS(status)) { int len = str.length(); const char* data = str.data(); if (strncmp(data, "NaN", 3) == 0 || strncmp(data, "Inf", 3) == 0 || strncmp(data, "-Inf", 4) == 0) { double resultDouble = res.getDouble(status); return doubleValueOf(env, (jdouble) resultDouble); } return newBigDecimal(env, data, len); } return NULL; } Formattable::Type numType = res.getType(); switch (numType) { case Formattable::kDouble: { double resultDouble = res.getDouble(); return doubleValueOf(env, (jdouble) resultDouble); } case Formattable::kLong: { long resultLong = res.getLong(); return longValueOf(env, (jlong) resultLong); } case Formattable::kInt64: { int64_t resultInt64 = res.getInt64(); return longValueOf(env, (jlong) resultInt64); } default: { return NULL; } } }
/** * Parses a string using the rule set or DecimalFormat belonging * to this substitution. If there's a match, a mathematical * operation (the inverse of the one used in formatting) is * performed on the result of the parse and the value passed in * and returned as the result. The parse position is updated to * point to the first unmatched character in the string. * @param text The string to parse * @param parsePosition On entry, ignored, but assumed to be 0. * On exit, this is updated to point to the first unmatched * character (or 0 if the substitution didn't match) * @param baseValue A partial parse result that should be * combined with the result of this parse * @param upperBound When searching the rule set for a rule * matching the string passed in, only rules with base values * lower than this are considered * @param lenientParse If true and matching against rules fails, * the substitution will also try matching the text against * numerals using a default-costructed NumberFormat. If false, * no extra work is done. (This value is false whenever the * formatter isn't in lenient-parse mode, but is also false * under some conditions even when the formatter _is_ in * lenient-parse mode.) * @return If there's a match, this is the result of composing * baseValue with whatever was returned from matching the * characters. This will be either a Long or a Double. If there's * no match this is new Long(0) (not null), and parsePosition * is left unchanged. */ UBool NFSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double upperBound, UBool lenientParse, Formattable& result) const { #ifdef RBNF_DEBUG fprintf(stderr, "<nfsubs> %x bv: %g ub: %g\n", this, baseValue, upperBound); #endif // figure out the highest base value a rule can have and match // the text being parsed (this varies according to the type of // substitutions: multiplier, modulus, and numerator substitutions // restrict the search to rules with base values lower than their // own; same-value substitutions leave the upper bound wherever // it was, and the others allow any rule to match upperBound = calcUpperBound(upperBound); // use our rule set to parse the text. If that fails and // lenient parsing is enabled (this is always false if the // formatter's lenient-parsing mode is off, but it may also // be false even when the formatter's lenient-parse mode is // on), then also try parsing the text using a default- // constructed NumberFormat if (ruleSet != NULL) { ruleSet->parse(text, parsePosition, upperBound, result); if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) { UErrorCode status = U_ZERO_ERROR; NumberFormat* fmt = NumberFormat::createInstance(status); if (U_SUCCESS(status)) { fmt->parse(text, result, parsePosition); } delete fmt; } // ...or use our DecimalFormat to parse the text } else if (numberFormat != NULL) { numberFormat->parse(text, result, parsePosition); } // if the parse was successful, we've already advanced the caller's // parse position (this is the one function that doesn't have one // of its own). Derive a parse result and return it as a Long, // if possible, or a Double if (parsePosition.getIndex() != 0) { UErrorCode status = U_ZERO_ERROR; double tempResult = result.getDouble(status); // composeRuleValue() produces a full parse result from // the partial parse result passed to this function from // the caller (this is either the owning rule's base value // or the partial result obtained from composing the // owning rule's base value with its other substitution's // parse result) and the partial parse result obtained by // matching the substitution (which will be the same value // the caller would get by parsing just this part of the // text with RuleBasedNumberFormat.parse() ). How the two // values are used to derive the full parse result depends // on the types of substitutions: For a regular rule, the // ultimate result is its multiplier substitution's result // times the rule's divisor (or the rule's base value) plus // the modulus substitution's result (which will actually // supersede part of the rule's base value). For a negative- // number rule, the result is the negative of its substitution's // result. For a fraction rule, it's the sum of its two // substitution results. For a rule in a fraction rule set, // it's the numerator substitution's result divided by // the rule's base value. Results from same-value substitutions // propagate back upard, and null substitutions don't affect // the result. tempResult = composeRuleValue(tempResult, baseValue); result.setDouble(tempResult); return TRUE; // if the parse was UNsuccessful, return 0 } else { result.setLong(0); return FALSE; } }
void TestMessageFormat::testParse() { UErrorCode err = U_ZERO_ERROR; int32_t count; UnicodeString msgFormatString = "{0} =sep= {1}"; MessageFormat msg( msgFormatString, err); UnicodeString source = "abc =sep= def"; UnicodeString tmp1, tmp2; Formattable* fmt_arr = msg.parse( source, count, err ); if (U_FAILURE(err) || (!fmt_arr)) { errln("*** MSG parse (ustring, count, err) error."); }else{ logln("MSG parse -- count: %d", count); if (count != 2) { errln("*** MSG parse (ustring, count, err) count err."); }else{ if ((fmt_arr[0].getType() == Formattable::kString) && (fmt_arr[1].getType() == Formattable::kString) && (fmt_arr[0].getString(tmp1) == "abc") && (fmt_arr[1].getString(tmp2) == "def")) { logln("MSG parse (ustring, count, err) tested."); }else{ errln("*** MSG parse (ustring, count, err) result err."); } } } delete[] fmt_arr; ParsePosition pp(0); fmt_arr = msg.parse( source, pp, count ); if ((pp == 0) || (!fmt_arr)) { errln("*** MSG parse (ustring, parsepos., count) error."); }else{ logln("MSG parse -- count: %d", count); if (count != 2) { errln("*** MSG parse (ustring, parsepos., count) count err."); }else{ if ((fmt_arr[0].getType() == Formattable::kString) && (fmt_arr[1].getType() == Formattable::kString) && (fmt_arr[0].getString(tmp1) == "abc") && (fmt_arr[1].getString(tmp2) == "def")) { logln("MSG parse (ustring, parsepos., count) tested."); }else{ errln("*** MSG parse (ustring, parsepos., count) result err."); } } } delete[] fmt_arr; pp = 0; Formattable fmta; msg.parseObject( source, fmta, pp ); if (pp == 0) { errln("*** MSG parse (ustring, Formattable, parsepos ) error."); }else{ logln("MSG parse -- count: %d", count); fmta.getArray(count); if (count != 2) { errln("*** MSG parse (ustring, Formattable, parsepos ) count err."); }else{ if ((fmta[0].getType() == Formattable::kString) && (fmta[1].getType() == Formattable::kString) && (fmta[0].getString(tmp1) == "abc") && (fmta[1].getString(tmp2) == "def")) { logln("MSG parse (ustring, Formattable, parsepos ) tested."); }else{ errln("*** MSG parse (ustring, Formattable, parsepos ) result err."); } } } }
UBool FractionalPartSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double /*upperBound*/, UBool lenientParse, Formattable& resVal) const { // if we're not in byDigits mode, we can just use the inherited // doParse() if (!byDigits) { return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); // if we ARE in byDigits mode, parse the text one digit at a time // using this substitution's owning rule set (we do this by setting // upperBound to 10 when calling doParse() ) until we reach // nonmatching text } else { UnicodeString workText(text); ParsePosition workPos(1); double result = 0; int32_t digit; // double p10 = 0.1; DigitList dl; NumberFormat* fmt = NULL; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); Formattable temp; getRuleSet()->parse(workText, workPos, 10, temp); UErrorCode status = U_ZERO_ERROR; digit = temp.getLong(status); // digit = temp.getType() == Formattable::kLong ? // temp.getLong() : // (int32_t)temp.getDouble(); if (lenientParse && workPos.getIndex() == 0) { if (!fmt) { status = U_ZERO_ERROR; fmt = NumberFormat::createInstance(status); if (U_FAILURE(status)) { delete fmt; fmt = NULL; } } if (fmt) { fmt->parse(workText, temp, workPos); digit = temp.getLong(status); } } if (workPos.getIndex() != 0) { dl.append((char)('0' + digit)); // result += digit * p10; // p10 /= 10; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); workText.removeBetween(0, workPos.getIndex()); while (workText.length() > 0 && workText.charAt(0) == gSpace) { workText.removeBetween(0, 1); parsePosition.setIndex(parsePosition.getIndex() + 1); } } } delete fmt; result = dl.fCount == 0 ? 0 : dl.getDouble(); result = composeRuleValue(result, baseValue); resVal.setDouble(result); return TRUE; } }
UBool NumeratorSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double upperBound, UBool /*lenientParse*/, Formattable& result) const { // we don't have to do anything special to do the parsing here, // but we have to turn lenient parsing off-- if we leave it on, // it SERIOUSLY messes up the algorithm // if withZeros is true, we need to count the zeros // and use that to adjust the parse result UErrorCode status = U_ZERO_ERROR; int32_t zeroCount = 0; UnicodeString workText(text); if (withZeros) { ParsePosition workPos(1); Formattable temp; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all if (workPos.getIndex() == 0) { // we failed, either there were no more zeros, or the number was formatted with digits // either way, we're done break; } ++zeroCount; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); workText.remove(0, workPos.getIndex()); while (workText.length() > 0 && workText.charAt(0) == gSpace) { workText.remove(0, 1); parsePosition.setIndex(parsePosition.getIndex() + 1); } } workText = text; workText.remove(0, (int32_t)parsePosition.getIndex()); parsePosition.setIndex(0); } // we've parsed off the zeros, now let's parse the rest from our current position NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result); if (withZeros) { // any base value will do in this case. is there a way to // force this to not bother trying all the base values? // compute the 'effective' base and prescale the value down int64_t n = result.getLong(status); // force conversion! int64_t d = 1; int32_t pow = 0; while (d <= n) { d *= 10; ++pow; } // now add the zeros while (zeroCount > 0) { d *= 10; --zeroCount; } // d is now our true denominator result.setDouble((double)n/(double)d); } return TRUE; }
std::string GlobalizationNDK::stringToNumber(const std::string& args) { if (args.empty()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: no arguments provided!"); return errorInJson(PARSING_ERROR, "No arguments provided!"); } Json::Reader reader; Json::Value root; bool parse = reader.parse(args, root); if (!parse) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: invalid json data: %s", args.c_str()); return errorInJson(PARSING_ERROR, "Invalid json data!"); } Json::Value sv = root["numberString"]; if (sv.isNull()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: no numberString provided!"); return errorInJson(FORMATTING_ERROR, "No numberString provided!"); } if (!sv.isString()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: invalid numberString type: %d!", sv.type()); return errorInJson(FORMATTING_ERROR, "Invalid numberString type!"); } std::string str = sv.asString(); if (str.empty()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: empty numberString!"); return errorInJson(FORMATTING_ERROR, "Empty numberString!"); } // This is the default value when no options provided. ENumberType type = kNumberDecimal; Json::Value options = root["options"]; std::string error; if (!handleNumberOptions(options, type, error)) return errorInJson(PARSING_ERROR, error); UErrorCode status = U_ZERO_ERROR; NumberFormat* nf; switch (type) { case kNumberDecimal: default: nf = NumberFormat::createInstance(status); break; case kNumberCurrency: nf = NumberFormat::createCurrencyInstance(status); break; case kNumberPercent: nf = NumberFormat::createPercentInstance(status); break; } if (!nf) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: failed to create NumberFormat instance for type %d: %d", status, type); return errorInJson(UNKNOWN_ERROR, "Failed to create NumberFormat instance!"); } std::auto_ptr<NumberFormat> deleter(nf); UnicodeString uStr = UnicodeString::fromUTF8(str); Formattable value; if (type == kNumberCurrency) { ParsePosition pos; CurrencyAmount* ca = nf->parseCurrency(uStr, pos); if (ca) value = ca->getNumber(); else nf->parse(uStr, value, status); } else nf->parse(uStr, value, status); if (status != U_ZERO_ERROR && status != U_ERROR_WARNING_START) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: failed (%d) to parse string: %s", status, str.c_str()); return errorInJson(PARSING_ERROR, "Failed to parse string!"); } if (!value.isNumeric()) { slog2f(0, ID_G11N, SLOG2_ERROR, "GlobalizationNDK::stringToNumber: string is not numeric: %s", str.c_str()); return errorInJson(FORMATTING_ERROR, "String is not numeric!"); } return resultInJson(value.getDouble()); }
void DecimalFormatTest::execFormatTest(int32_t lineNum, const UnicodeString &pattern, // Pattern const UnicodeString &round, // rounding mode const UnicodeString &input, // input decimal number const UnicodeString &expected, // expected formatted result UErrorCode &status) { if (U_FAILURE(status)) { return; } DecimalFormatSymbols symbols(Locale::getUS(), status); // printf("Pattern = %s\n", UnicodeStringPiece(pattern).data()); DecimalFormat fmtr(pattern, symbols, status); if (U_FAILURE(status)) { dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.", lineNum, u_errorName(status)); return; } if (round=="ceiling") { fmtr.setRoundingMode(DecimalFormat::kRoundCeiling); } else if (round=="floor") { fmtr.setRoundingMode(DecimalFormat::kRoundFloor); } else if (round=="down") { fmtr.setRoundingMode(DecimalFormat::kRoundDown); } else if (round=="up") { fmtr.setRoundingMode(DecimalFormat::kRoundUp); } else if (round=="halfeven") { fmtr.setRoundingMode(DecimalFormat::kRoundHalfEven); } else if (round=="halfdown") { fmtr.setRoundingMode(DecimalFormat::kRoundHalfDown); } else if (round=="halfup") { fmtr.setRoundingMode(DecimalFormat::kRoundHalfUp); } else if (round=="default") { // don't set any value. } else if (round=="unnecessary") { fmtr.setRoundingMode(DecimalFormat::kRoundUnnecessary); } else { fmtr.setRoundingMode(DecimalFormat::kRoundFloor); errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"", lineNum, UnicodeStringPiece(round).data()); } UnicodeString result; UnicodeStringPiece spInput(input); //fmtr.format(spInput, result, NULL, status); Formattable fmtbl; fmtbl.setDecimalNumber(spInput, status); //NumberFormat &nfmtr = fmtr; fmtr.format(fmtbl, result, NULL, status); if ((status == U_FORMAT_INEXACT_ERROR) && (result == "") && (expected == "Inexact")) { // Test succeeded. status = U_ZERO_ERROR; return; } if (U_FAILURE(status)) { errln("file dcfmtest.txt, line %d: format() returned %s.", lineNum, u_errorName(status)); status = U_ZERO_ERROR; return; } if (result != expected) { errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", lineNum, UnicodeStringPiece(expected).data(), UnicodeStringPiece(result).data()); } }
void DecimalFormatTest::execParseTest(int32_t lineNum, const UnicodeString &inputText, const UnicodeString &expectedType, const UnicodeString &expectedDecimal, UErrorCode &status) { if (U_FAILURE(status)) { return; } DecimalFormatSymbols symbols(Locale::getUS(), status); UnicodeString pattern = UNICODE_STRING_SIMPLE("####"); DecimalFormat format(pattern, symbols, status); Formattable result; if (U_FAILURE(status)) { dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.", lineNum, u_errorName(status)); return; } ParsePosition pos; int32_t expectedParseEndPosition = inputText.length(); format.parse(inputText, result, pos); if (expectedParseEndPosition != pos.getIndex()) { errln("file dcfmtest.txt, line %d: Expected parse position afeter parsing: %d. " "Actual parse position: %d", expectedParseEndPosition, pos.getIndex()); return; } char expectedTypeC[2]; expectedType.extract(0, 1, expectedTypeC, 2, US_INV); Formattable::Type expectType = Formattable::kDate; switch (expectedTypeC[0]) { case 'd': expectType = Formattable::kDouble; break; case 'i': expectType = Formattable::kLong; break; case 'l': expectType = Formattable::kInt64; break; default: errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"", lineNum, InvariantStringPiece(expectedType).data()); return; } if (result.getType() != expectType) { errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parseType(%s)", lineNum, formattableType(expectType), formattableType(result.getType())); return; } StringPiece decimalResult = result.getDecimalNumber(status); if (U_FAILURE(status)) { errln("File %s, line %d: error %s. Line in file dcfmtest.txt: %d:", __FILE__, __LINE__, u_errorName(status), lineNum); return; } InvariantStringPiece expectedResults(expectedDecimal); if (decimalResult != expectedResults) { errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"", lineNum, expectedResults.data(), decimalResult.data()); } return; }
UBool NFRuleSet::parse(const UnicodeString & text, ParsePosition & pos, double upperBound, Formattable & result) const { // try matching each rule in the rule set against the text being // parsed. Whichever one matches the most characters is the one // that determines the value we return. result.setLong(0); // dump out if there's no text to parse if (text.length() == 0) { return 0; } ParsePosition highWaterMark; ParsePosition workingPos = pos; #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> %x '", this); dumpUS(stderr, name); fprintf(stderr, "' text '"); dumpUS(stderr, text); fprintf(stderr, "'\n"); fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0); #endif // start by trying the negative number rule (if there is one) if (negativeNumberRule) { Formattable tempResult; #ifdef RBNF_DEBUG fprintf(stderr, " <nfrs before negative> %x ub: %g\n", negativeNumberRule, upperBound); #endif UBool success = negativeNumberRule->doParse(text, workingPos, 0, upperBound, tempResult); #ifdef RBNF_DEBUG fprintf(stderr, " <nfrs after negative> success: %d wpi: %d\n", success, workingPos.getIndex()); #endif if (success && workingPos.getIndex() > highWaterMark.getIndex()) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> continue fractional with text '"); dumpUS(stderr, text); fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); #endif // then try each of the fraction rules { for (int i = 0; i < 3; i++) { if (fractionRules[i]) { Formattable tempResult; UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult); if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } } } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> continue other with text '"); dumpUS(stderr, text); fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); #endif // finally, go through the regular rules one at a time. We start // at the end of the list because we want to try matching the most // sigificant rule first (this helps ensure that we parse // "five thousand three hundred six" as // "(five thousand) (three hundred) (six)" rather than // "((five thousand three) hundred) (six)"). Skip rules whose // base values are higher than the upper bound (again, this helps // limit ambiguity by making sure the rules that match a rule's // are less significant than the rule containing the substitutions)/ { int64_t ub = util64_fromDouble(upperBound); #ifdef RBNF_DEBUG { char ubstr[64]; util64_toa(ub, ubstr, 64); char ubstrhex[64]; util64_toa(ub, ubstrhex, 64, 16); fprintf(stderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex); } #endif for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) { if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) { continue; } Formattable tempResult; UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, tempResult); if (success && workingPos.getIndex() > highWaterMark.getIndex()) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> exit\n"); #endif // finally, update the parse postion we were passed to point to the // first character we didn't use, and return the result that // corresponds to that string of characters pos = highWaterMark; return 1; }
/* * Initializes the region data from the ICU resource bundles. The region data * contains the basic relationships such as which regions are known, what the numeric * codes are, any known aliases, and the territory containment data. * * If the region data has already loaded, then this method simply returns without doing * anything meaningful. */ void Region::loadRegionData(UErrorCode &status) { // Construct service objs first LocalUHashtablePointer newRegionIDMap(uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status)); LocalUHashtablePointer newNumericCodeMap(uhash_open(uhash_hashLong,uhash_compareLong,NULL,&status)); LocalUHashtablePointer newRegionAliases(uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status)); LocalPointer<DecimalFormat> df(new DecimalFormat(status), status); LocalPointer<UVector> continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); LocalPointer<UVector> groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); allRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); LocalUResourceBundlePointer metadata(ures_openDirect(NULL,"metadata",&status)); LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(),"alias",NULL,&status)); LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(),"territory",NULL,&status)); LocalUResourceBundlePointer supplementalData(ures_openDirect(NULL,"supplementalData",&status)); LocalUResourceBundlePointer codeMappings(ures_getByKey(supplementalData.getAlias(),"codeMappings",NULL,&status)); LocalUResourceBundlePointer idValidity(ures_getByKey(supplementalData.getAlias(),"idValidity",NULL,&status)); LocalUResourceBundlePointer regionList(ures_getByKey(idValidity.getAlias(),"region",NULL,&status)); LocalUResourceBundlePointer regionRegular(ures_getByKey(regionList.getAlias(),"regular",NULL,&status)); LocalUResourceBundlePointer regionMacro(ures_getByKey(regionList.getAlias(),"macroregion",NULL,&status)); LocalUResourceBundlePointer regionUnknown(ures_getByKey(regionList.getAlias(),"unknown",NULL,&status)); LocalUResourceBundlePointer territoryContainment(ures_getByKey(supplementalData.getAlias(),"territoryContainment",NULL,&status)); LocalUResourceBundlePointer worldContainment(ures_getByKey(territoryContainment.getAlias(),"001",NULL,&status)); LocalUResourceBundlePointer groupingContainment(ures_getByKey(territoryContainment.getAlias(),"grouping",NULL,&status)); if (U_FAILURE(status)) { return; } // now, initialize df->setParseIntegerOnly(TRUE); uhash_setValueDeleter(newRegionIDMap.getAlias(), deleteRegion); // regionIDMap owns objs uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys while ( ures_hasNext(regionRegular.getAlias()) ) { UnicodeString regionName = ures_getNextUnicodeString(regionRegular.getAlias(),NULL,&status); int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); UChar buf[6]; regionName.extract(buf,6,status); if ( rangeMarkerLocation > 0 ) { UChar endRange = regionName.charAt(rangeMarkerLocation+1); buf[rangeMarkerLocation] = 0; while ( buf[rangeMarkerLocation-1] <= endRange ) { LocalPointer<UnicodeString> newRegion(new UnicodeString(buf), status); allRegions->addElement(newRegion.orphan(),status); buf[rangeMarkerLocation-1]++; } } else { LocalPointer<UnicodeString> newRegion(new UnicodeString(regionName), status); allRegions->addElement(newRegion.orphan(),status); } } while ( ures_hasNext(regionMacro.getAlias()) ) { UnicodeString regionName = ures_getNextUnicodeString(regionMacro.getAlias(),NULL,&status); int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER); UChar buf[6]; regionName.extract(buf,6,status); if ( rangeMarkerLocation > 0 ) { UChar endRange = regionName.charAt(rangeMarkerLocation+1); buf[rangeMarkerLocation] = 0; while ( buf[rangeMarkerLocation-1] <= endRange ) { LocalPointer<UnicodeString> newRegion(new UnicodeString(buf), status); allRegions->addElement(newRegion.orphan(),status); buf[rangeMarkerLocation-1]++; } } else { LocalPointer<UnicodeString> newRegion(new UnicodeString(regionName), status); allRegions->addElement(newRegion.orphan(),status); } } while ( ures_hasNext(regionUnknown.getAlias()) ) { LocalPointer<UnicodeString> regionName (new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(),NULL,&status),status)); allRegions->addElement(regionName.orphan(),status); } while ( ures_hasNext(worldContainment.getAlias()) ) { UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment.getAlias(),NULL,&status)); continents->addElement(continentName,status); } while ( ures_hasNext(groupingContainment.getAlias()) ) { UnicodeString *groupingName = new UnicodeString(ures_getNextUnicodeString(groupingContainment.getAlias(),NULL,&status)); groupings->addElement(groupingName,status); } for ( int32_t i = 0 ; i < allRegions->size() ; i++ ) { LocalPointer<Region> r(new Region(), status); if ( U_FAILURE(status) ) { return; } UnicodeString *regionName = (UnicodeString *)allRegions->elementAt(i); r->idStr = *regionName; r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV); r->type = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known. Formattable result; UErrorCode ps = U_ZERO_ERROR; df->parse(r->idStr,result,ps); if ( U_SUCCESS(ps) ) { r->code = result.getLong(); // Convert string to number uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)(r.getAlias()),&status); r->type = URGN_SUBCONTINENT; } else { r->code = -1; } void* idStrAlias = (void*)&(r->idStr); // about to orphan 'r'. Save this off. uhash_put(newRegionIDMap.getAlias(),idStrAlias,(void *)(r.orphan()),&status); // regionIDMap takes ownership } // Process the territory aliases while ( ures_hasNext(territoryAlias.getAlias()) ) { LocalUResourceBundlePointer res(ures_getNextResource(territoryAlias.getAlias(),NULL,&status)); const char *aliasFrom = ures_getKey(res.getAlias()); LocalPointer<UnicodeString> aliasFromStr(new UnicodeString(aliasFrom, -1, US_INV), status); UnicodeString aliasTo = ures_getUnicodeStringByKey(res.getAlias(),"replacement",&status); res.adoptInstead(NULL); const Region *aliasToRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),&aliasTo); Region *aliasFromRegion = (Region *)uhash_get(newRegionIDMap.getAlias(),aliasFromStr.getAlias()); if ( aliasToRegion != NULL && aliasFromRegion == NULL ) { // This is just an alias from some string to a region uhash_put(newRegionAliases.getAlias(),(void *)aliasFromStr.orphan(), (void *)aliasToRegion,&status); } else { if ( aliasFromRegion == NULL ) { // Deprecated region code not in the master codes list - so need to create a deprecated region for it. LocalPointer<Region> newRgn(new Region, status); if ( U_SUCCESS(status) ) { aliasFromRegion = newRgn.orphan(); } else { return; // error out } aliasFromRegion->idStr.setTo(*aliasFromStr); aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV); uhash_put(newRegionIDMap.getAlias(),(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status); Formattable result; UErrorCode ps = U_ZERO_ERROR; df->parse(aliasFromRegion->idStr,result,ps); if ( U_SUCCESS(ps) ) { aliasFromRegion->code = result.getLong(); // Convert string to number uhash_iput(newNumericCodeMap.getAlias(),aliasFromRegion->code,(void *)aliasFromRegion,&status); } else { aliasFromRegion->code = -1; } aliasFromRegion->type = URGN_DEPRECATED; } else { aliasFromRegion->type = URGN_DEPRECATED; } { LocalPointer<UVector> newPreferredValues(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); aliasFromRegion->preferredValues = newPreferredValues.orphan(); } if( U_FAILURE(status)) { return; } UnicodeString currentRegion; //currentRegion.remove(); TODO: was already 0 length? for (int32_t i = 0 ; i < aliasTo.length() ; i++ ) { if ( aliasTo.charAt(i) != 0x0020 ) { currentRegion.append(aliasTo.charAt(i)); } if ( aliasTo.charAt(i) == 0x0020 || i+1 == aliasTo.length() ) { Region *target = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)¤tRegion); if (target) { LocalPointer<UnicodeString> preferredValue(new UnicodeString(target->idStr), status); aliasFromRegion->preferredValues->addElement((void *)preferredValue.orphan(),status); // may add null if err } currentRegion.remove(); } } } } // Process the code mappings - This will allow us to assign numeric codes to most of the territories. while ( ures_hasNext(codeMappings.getAlias()) ) { UResourceBundle *mapping = ures_getNextResource(codeMappings.getAlias(),NULL,&status); if ( ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status); UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status); UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status); Region *r = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)&codeMappingID); if ( r ) { Formattable result; UErrorCode ps = U_ZERO_ERROR; df->parse(codeMappingNumber,result,ps); if ( U_SUCCESS(ps) ) { r->code = result.getLong(); // Convert string to number uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)r,&status); } LocalPointer<UnicodeString> code3(new UnicodeString(codeMapping3Letter), status); uhash_put(newRegionAliases.getAlias(),(void *)code3.orphan(), (void *)r,&status); } } ures_close(mapping); } // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS Region *r; UnicodeString WORLD_ID_STRING(WORLD_ID); r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&WORLD_ID_STRING); if ( r ) { r->type = URGN_WORLD; } UnicodeString UNKNOWN_REGION_ID_STRING(UNKNOWN_REGION_ID); r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&UNKNOWN_REGION_ID_STRING); if ( r ) { r->type = URGN_UNKNOWN; } for ( int32_t i = 0 ; i < continents->size() ; i++ ) { r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)continents->elementAt(i)); if ( r ) { r->type = URGN_CONTINENT; } } for ( int32_t i = 0 ; i < groupings->size() ; i++ ) { r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)groupings->elementAt(i)); if ( r ) { r->type = URGN_GROUPING; } } // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR // even though it looks like a territory code. Need to handle it here. UnicodeString OUTLYING_OCEANIA_REGION_ID_STRING(OUTLYING_OCEANIA_REGION_ID); r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&OUTLYING_OCEANIA_REGION_ID_STRING); if ( r ) { r->type = URGN_SUBCONTINENT; } // Load territory containment info from the supplemental data. while ( ures_hasNext(territoryContainment.getAlias()) ) { LocalUResourceBundlePointer mapping(ures_getNextResource(territoryContainment.getAlias(),NULL,&status)); if( U_FAILURE(status) ) { return; // error out } const char *parent = ures_getKey(mapping.getAlias()); if (uprv_strcmp(parent, "containedGroupings") == 0 || uprv_strcmp(parent, "deprecated") == 0) { continue; // handle new pseudo-parent types added in ICU data per cldrbug 7808; for now just skip. // #11232 is to do something useful with these. } UnicodeString parentStr = UnicodeString(parent, -1 , US_INV); Region *parentRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&parentStr); for ( int j = 0 ; j < ures_getSize(mapping.getAlias()); j++ ) { UnicodeString child = ures_getUnicodeStringByIndex(mapping.getAlias(),j,&status); Region *childRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&child); if ( parentRegion != NULL && childRegion != NULL ) { // Add the child region to the set of regions contained by the parent if (parentRegion->containedRegions == NULL) { parentRegion->containedRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); } LocalPointer<UnicodeString> childStr(new UnicodeString(), status); if( U_FAILURE(status) ) { return; // error out } childStr->fastCopyFrom(childRegion->idStr); parentRegion->containedRegions->addElement((void *)childStr.orphan(),status); // Set the parent region to be the containing region of the child. // Regions of type GROUPING can't be set as the parent, since another region // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent. if ( parentRegion->type != URGN_GROUPING) { childRegion->containingRegion = parentRegion; } } } } // Create the availableRegions lists int32_t pos = UHASH_FIRST; while ( const UHashElement* element = uhash_nextElement(newRegionIDMap.getAlias(),&pos)) { Region *ar = (Region *)element->value.pointer; if ( availableRegions[ar->type] == NULL ) { LocalPointer<UVector> newAr(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status); availableRegions[ar->type] = newAr.orphan(); } LocalPointer<UnicodeString> arString(new UnicodeString(ar->idStr), status); if( U_FAILURE(status) ) { return; // error out } availableRegions[ar->type]->addElement((void *)arString.orphan(),status); } ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup); // copy hashtables numericCodeMap = newNumericCodeMap.orphan(); regionIDMap = newRegionIDMap.orphan(); regionAliases = newRegionAliases.orphan(); }
void PluralFormatTest::pluralFormatExtendedTest(void) { const char *targets[] = { "There are no widgets.", "There is one widget.", "There is a bling widget and one other widget.", "There is a bling widget and 2 other widgets.", "There is a bling widget and 3 other widgets.", "Widgets, five (5-1=4) there be.", "There is a bling widget and 5 other widgets.", "There is a bling widget and 6 other widgets.", }; const char* fmt = "offset:1.0 " "=0 {There are no widgets.} " "=1.0 {There is one widget.} " "=5 {Widgets, five (5-1=#) there be.} " "one {There is a bling widget and one other widget.} " "other {There is a bling widget and # other widgets.}"; UErrorCode status = U_ZERO_ERROR; UnicodeString fmtString(fmt, -1, US_INV); PluralFormat pf(Locale::getEnglish(), fmtString, status); MessageFormat mf(UNICODE_STRING_SIMPLE("{0,plural,").append(fmtString).append((UChar)0x7d /* '}' */), Locale::getEnglish(), status); Formattable args; FieldPosition ignore; if (U_FAILURE(status)) { dataerrln("Failed to apply pattern - %s", u_errorName(status)); return; } for (int32_t i = 0; i < 7; ++i) { UnicodeString result = pf.format(i, status); if (U_FAILURE(status)) { errln("PluralFormat.format(value %d) failed - %s", i, u_errorName(status)); return; } UnicodeString expected(targets[i], -1, US_INV); if (expected != result) { UnicodeString message("PluralFormat.format(): Expected '", -1, US_INV); message.append(expected); message.append(UnicodeString("' but got '", -1, US_INV)); message.append(result); message.append("'", -1, US_INV); errln(message); } args.setLong(i); mf.format(&args, 1, result.remove(), ignore, status); if (U_FAILURE(status)) { errln("MessageFormat.format(value %d) failed - %s", i, u_errorName(status)); return; } if (expected != result) { UnicodeString message("MessageFormat.format(): Expected '", -1, US_INV); message.append(expected); message.append(UnicodeString("' but got '", -1, US_INV)); message.append(result); message.append("'", -1, US_INV); errln(message); } } }
static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, jobject position, jboolean lenient) { // LOGI("ENTER parseRBNFImpl"); const char * parsePositionClassName = "java/text/ParsePosition"; const char * longClassName = "java/lang/Long"; const char * doubleClassName = "java/lang/Double"; UErrorCode status = U_ZERO_ERROR; UNumberFormat *fmt = (UNumberFormat *)(int)addr; jchar *str = (UChar *)env->GetStringChars(text, NULL); int strlength = env->GetStringLength(text); jclass parsePositionClass = env->FindClass(parsePositionClassName); jclass longClass = env->FindClass(longClassName); jclass doubleClass = env->FindClass(doubleClassName); jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, "getIndex", "()I"); jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, "setIndex", "(I)V"); jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, "setErrorIndex", "(I)V"); jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V"); int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); // make sure the ParsePosition is valid. Actually icu4c would parse a number // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too if(parsePos < 0 || parsePos > strlength) { return NULL; } Formattable res; const UnicodeString src((UChar*)str, strlength, strlength); ParsePosition pp; pp.setIndex(parsePos); if(lenient) { unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE); } ((const NumberFormat*)fmt)->parse(src, res, pp); if(lenient) { unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_FALSE); } env->ReleaseStringChars(text, str); if(pp.getErrorIndex() == -1) { parsePos = pp.getIndex(); } else { env->CallVoidMethod(position, setErrorIndexMethodID, (jint) pp.getErrorIndex()); return NULL; } Formattable::Type numType; numType = res.getType(); UErrorCode fmtStatus; double resultDouble; long resultLong; int64_t resultInt64; switch(numType) { case Formattable::kDouble: resultDouble = res.getDouble(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(doubleClass, dblInitMethodID, (jdouble) resultDouble); case Formattable::kLong: resultLong = res.getLong(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(longClass, longInitMethodID, (jlong) resultLong); case Formattable::kInt64: resultInt64 = res.getInt64(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); return env->NewObject(longClass, longInitMethodID, (jlong) resultInt64); default: break; } return NULL; }
void test_Formattable( void ) { UErrorCode status = U_ZERO_ERROR; Formattable* ftp = new Formattable(); if (!ftp || !(ftp->getType() == Formattable::kLong) || !(ftp->getLong() == 0)) { it_errln("*** Formattable constructor or getType or getLong"); } delete ftp; Formattable fta, ftb; fta.setLong(1); ftb.setLong(2); if ((fta != ftb) || !(fta == ftb)) { it_logln("FT setLong, operator== and operator!= tested."); status = U_ZERO_ERROR; fta.getLong(&status); if ( status == U_INVALID_FORMAT_ERROR){ it_errln("*** FT getLong(UErrorCode* status) failed on real Long"); } else { it_logln("FT getLong(UErrorCode* status) tested."); } }else{ it_errln("*** Formattable setLong or operator== or !="); } fta = ftb; if ((fta == ftb) || !(fta != ftb)) { it_logln("FT operator= tested."); }else{ it_errln("*** FT operator= or operator== or operator!="); } fta.setDouble( 3.0 ); if ((fta.getType() == Formattable::kDouble) && (fta.getDouble() == 3.0)) { it_logln("FT set- and getDouble tested."); }else{ it_errln("*** FT set- or getDouble"); } fta.getDate(status = U_ZERO_ERROR); if (status != U_INVALID_FORMAT_ERROR){ it_errln("*** FT getDate with status should fail on non-Date"); } fta.setDate( 4.0 ); if ((fta.getType() == Formattable::kDate) && (fta.getDate() == 4.0)) { it_logln("FT set- and getDate tested."); status = U_ZERO_ERROR; fta.getDate(status); if ( status == U_INVALID_FORMAT_ERROR){ it_errln("*** FT getDate with status failed on real Date"); } else { it_logln("FT getDate with status tested."); } }else{ it_errln("*** FT set- or getDate"); } status = U_ZERO_ERROR; fta.getLong(&status); if (status != U_INVALID_FORMAT_ERROR){ it_errln("*** FT getLong(UErrorCode* status) should fail on non-Long"); } fta.setString("abc"); const Formattable ftc(fta); UnicodeString res; { UBool t; t = (fta.getType() == Formattable::kString) && (fta.getString(res) == "abc") && (fta.getString() == "abc"); res = fta.getString(status = U_ZERO_ERROR); t = t && (status != U_INVALID_FORMAT_ERROR && res == "abc"); res = ftc.getString(status = U_ZERO_ERROR); t = t && (status != U_INVALID_FORMAT_ERROR && res == "abc"); ftc.getString(res,status = U_ZERO_ERROR); t = t && (status != U_INVALID_FORMAT_ERROR && res == "abc"); if (t) { it_logln("FT set- and getString tested."); }else{ it_errln("*** FT set- or getString"); } } UnicodeString ucs = "unicode-string"; UnicodeString* ucs_ptr = new UnicodeString("pointed-to-unicode-string"); const Formattable ftarray[] = { Formattable( 1.0, Formattable::kIsDate ), 2.0, (int32_t)3, ucs, ucs_ptr }; const int32_t ft_cnt = UPRV_LENGTHOF(ftarray); Formattable ft_arr( ftarray, ft_cnt ); UnicodeString temp; if ((ft_arr[0].getType() == Formattable::kDate) && (ft_arr[0].getDate() == 1.0) && (ft_arr[1].getType() == Formattable::kDouble) && (ft_arr[1].getDouble() == 2.0) && (ft_arr[2].getType() == Formattable::kLong) && (ft_arr[2].getLong() == (int32_t)3) && (ft_arr[3].getType() == Formattable::kString) && (ft_arr[3].getString(temp) == ucs) && (ft_arr[4].getType() == Formattable::kString) && (ft_arr[4].getString(temp) == *ucs_ptr) ) { it_logln("FT constr. for date, double, long, ustring, ustring* and array tested"); }else{ it_errln("*** FT constr. for date, double, long, ustring, ustring* or array"); } int32_t i, res_cnt; const Formattable* res_array = ft_arr.getArray( res_cnt ); if (res_cnt == ft_cnt) { UBool same = TRUE; for (i = 0; i < res_cnt; i++ ) { if (res_array[i] != ftarray[i]) { same = FALSE; } } if (same) { it_logln("FT getArray tested"); res_array = ft_arr.getArray( res_cnt, status = U_ZERO_ERROR); if (status == U_INVALID_FORMAT_ERROR){ it_errln("*** FT getArray with status failed on real array"); } else { it_logln("FT getArray with status tested on real array"); } }else{ it_errln("*** FT getArray comparison"); } }else{ it_errln(UnicodeString("*** FT getArray count res_cnt=") + res_cnt + UnicodeString("ft_cnt=") + ft_cnt); } res_array = fta.getArray(res_cnt, status = U_ZERO_ERROR); if (status == U_INVALID_FORMAT_ERROR){ if (res_cnt == 0 && res_array == NULL){ it_logln("FT getArray with status tested on non array"); } else { it_errln("*** FT getArray with status return values are not consistent"); } } else { it_errln("*** FT getArray with status should fail on non-array"); } Formattable *pf; for(i = 0; i < ft_cnt; ++i) { pf = ftarray[i].clone(); if(pf == (ftarray + i) || *pf != ftarray[i]) { it_errln(UnicodeString("Formattable.clone() failed for item ") + i); } delete pf; } const Formattable ftarr1[] = { Formattable( (int32_t)1 ), Formattable( (int32_t)2 ) }; const Formattable ftarr2[] = { Formattable( (int32_t)3 ), Formattable( (int32_t)4 ) }; const int32_t ftarr1_cnt = (int32_t)(sizeof(ftarr1) / sizeof(Formattable)); const int32_t ftarr2_cnt = (int32_t)(sizeof(ftarr2) / sizeof(Formattable)); ft_arr.setArray( ftarr1, ftarr1_cnt ); if ((ft_arr[0].getType() == Formattable::kLong) && (ft_arr[0].getLong() == (int32_t)1)) { it_logln("FT setArray tested"); }else{ it_errln("*** FT setArray"); } Formattable* ft_dynarr = new Formattable[ftarr2_cnt]; for (i = 0; i < ftarr2_cnt; i++ ) { ft_dynarr[i] = ftarr2[i]; } if ((ft_dynarr[0].getType() == Formattable::kLong) && (ft_dynarr[0].getLong() == (int32_t)3) && (ft_dynarr[1].getType() == Formattable::kLong) && (ft_dynarr[1].getLong() == (int32_t)4)) { it_logln("FT operator= and array operations tested"); }else{ it_errln("*** FT operator= or array operations"); } ft_arr.adoptArray( ft_dynarr, ftarr2_cnt ); if ((ft_arr[0].getType() == Formattable::kLong) && (ft_arr[0].getLong() == (int32_t)3) && (ft_arr[1].getType() == Formattable::kLong) && (ft_arr[1].getLong() == (int32_t)4)) { it_logln("FT adoptArray tested"); }else{ it_errln("*** FT adoptArray or operator[]"); } ft_arr.setLong(0); // calls 'dispose' and deletes adopted array ! UnicodeString* ucs_dyn = new UnicodeString("ttt"); UnicodeString tmp2; fta.adoptString( ucs_dyn ); if ((fta.getType() == Formattable::kString) && (fta.getString(tmp2) == "ttt")) { it_logln("FT adoptString tested"); }else{ it_errln("*** FT adoptString or getString"); } fta.setLong(0); // calls 'dispose' and deletes adopted string ! it_logln(); }
void MessageFormatRegressionTest::Test4031438() { UErrorCode status = U_ZERO_ERROR; UnicodeString pattern1("Impossible {1} has occurred -- status code is {0} and message is {2}."); UnicodeString pattern2("Double '' Quotes {0} test and quoted '{1}' test plus 'other {2} stuff'."); MessageFormat *messageFormatter = new MessageFormat("", status); failure(status, "new MessageFormat"); const UBool possibleDataError = TRUE; //try { logln("Apply with pattern : " + pattern1); messageFormatter->applyPattern(pattern1, status); failure(status, "messageFormat->applyPattern"); //Object[] params = {new Integer(7)}; Formattable params []= { Formattable((int32_t)7) }; UnicodeString tempBuffer; FieldPosition pos(FieldPosition::DONT_CARE); tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status); if(tempBuffer != "Impossible {1} has occurred -- status code is 7 and message is {2}." || failure(status, "MessageFormat::format")) dataerrln("Tests arguments < substitution failed"); logln("Formatted with 7 : " + tempBuffer); ParsePosition pp(0); int32_t count = 0; Formattable *objs = messageFormatter->parse(tempBuffer, pp, count); //if(objs[7/*params.length*/] != NULL) // errln("Parse failed with more than expected arguments"); NumberFormat *fmt = 0; UnicodeString temp, temp1; for (int i = 0; i < count; i++) { // convert to string if not already Formattable obj = objs[i]; temp.remove(); if(obj.getType() == Formattable::kString) temp = obj.getString(temp); else { fmt = NumberFormat::createInstance(status); switch (obj.getType()) { case Formattable::kLong: fmt->format(obj.getLong(), temp); break; case Formattable::kInt64: fmt->format(obj.getInt64(), temp); break; case Formattable::kDouble: fmt->format(obj.getDouble(), temp); break; default: break; } } // convert to string if not already Formattable obj1 = params[i]; temp1.remove(); if(obj1.getType() == Formattable::kString) temp1 = obj1.getString(temp1); else { fmt = NumberFormat::createInstance(status); switch (obj1.getType()) { case Formattable::kLong: fmt->format(obj1.getLong(), temp1); break; case Formattable::kInt64: fmt->format(obj1.getInt64(), temp1); break; case Formattable::kDouble: fmt->format(obj1.getDouble(), temp1); break; default: break; } } //if (objs[i] != NULL && objs[i].getString(temp1) != params[i].getString(temp2)) { if (temp != temp1) { errln("Parse failed on object " + objs[i].getString(temp1) + " at index : " + i); } } delete fmt; delete [] objs; // {sfb} does this apply? no way to really pass a null Formattable, // only a null array /*tempBuffer = messageFormatter->format(null, tempBuffer, FieldPosition(FieldPosition::DONT_CARE), status); if (tempBuffer != "Impossible {1} has occurred -- status code is {0} and message is {2}." || failure(status, "messageFormat->format")) errln("Tests with no arguments failed"); logln("Formatted with null : " + tempBuffer);*/ logln("Apply with pattern : " + pattern2); messageFormatter->applyPattern(pattern2, status); failure(status, "messageFormatter->applyPattern", possibleDataError); tempBuffer.remove(); tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status); if (tempBuffer != "Double ' Quotes 7 test and quoted {1} test plus other {2} stuff.") dataerrln("quote format test (w/ params) failed. - %s", u_errorName(status)); logln("Formatted with params : " + tempBuffer); /*tempBuffer = messageFormatter->format(null); if (!tempBuffer.equals("Double ' Quotes {0} test and quoted {1} test plus other {2} stuff.")) errln("quote format test (w/ null) failed."); logln("Formatted with null : " + tempBuffer); logln("toPattern : " + messageFormatter.toPattern());*/ /*} catch (Exception foo) { errln("Exception when formatting in bug 4031438. "+foo.getMessage()); }*/ delete messageFormatter; }
/* @bug 7902 * Tests for Greek Language. * This tests that requests for short unit names correctly fall back * to long unit names for a locale where the locale data does not * provide short unit names. As of CLDR 1.9, Greek is one such language. */ void TimeUnitTest::testGreekWithFallback() { UErrorCode status = U_ZERO_ERROR; const char* locales[] = {"el-GR", "el"}; TimeUnit::UTimeUnitFields tunits[] = {TimeUnit::UTIMEUNIT_SECOND, TimeUnit::UTIMEUNIT_MINUTE, TimeUnit::UTIMEUNIT_HOUR, TimeUnit::UTIMEUNIT_DAY, TimeUnit::UTIMEUNIT_MONTH, TimeUnit::UTIMEUNIT_YEAR}; UTimeUnitFormatStyle styles[] = {UTMUTFMT_FULL_STYLE, UTMUTFMT_ABBREVIATED_STYLE}; const int numbers[] = {1, 7}; const UChar oneSecond[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03bf, 0}; const UChar oneSecondShort[] = {0x0031, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0}; const UChar oneMinute[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03cc, 0}; const UChar oneMinuteShort[] = {0x0031, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0}; const UChar oneHour[] = {0x0031, 0x0020, 0x03ce, 0x03c1, 0x03b1, 0}; const UChar oneDay[] = {0x0031, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b1, 0}; const UChar oneMonth[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b1, 0x03c2, 0}; const UChar oneMonthShort[] = {0x0031, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0}; const UChar oneYear[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x03bf, 0x03c2, 0}; const UChar oneYearShort[] = {0x0031, 0x0020, 0x03ad, 0x03c4, 0x002e, 0}; const UChar sevenSeconds[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x03b5, 0x03c1, 0x03cc, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03b1, 0}; const UChar sevenSecondsShort[] = {0x0037, 0x0020, 0x03b4, 0x03b5, 0x03c5, 0x03c4, 0x002e, 0}; const UChar sevenMinutes[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x03c4, 0x03ac, 0}; const UChar sevenMinutesShort[] = {0x0037, 0x0020, 0x03bb, 0x03b5, 0x03c0, 0x002e, 0}; const UChar sevenHours[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x03b5, 0x03c2, 0}; const UChar sevenHoursShort[] = {0x0037, 0x0020, 0x03ce, 0x03c1, 0x002e, 0}; const UChar sevenDays[] = {0x0037, 0x0020, 0x03b7, 0x03bc, 0x03ad, 0x03c1, 0x03b5, 0x03c2, 0}; const UChar sevenMonths[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x03b5, 0x3c2, 0}; const UChar sevenMonthsShort[] = {0x0037, 0x0020, 0x03bc, 0x03ae, 0x03bd, 0x002e, 0}; const UChar sevenYears[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x03b7, 0}; const UChar sevenYearsShort[] = {0x0037, 0x0020, 0x03ad, 0x03c4, 0x002e, 0}; const UnicodeString oneSecondStr(oneSecond); const UnicodeString oneSecondShortStr(oneSecondShort); const UnicodeString oneMinuteStr(oneMinute); const UnicodeString oneMinuteShortStr(oneMinuteShort); const UnicodeString oneHourStr(oneHour); const UnicodeString oneDayStr(oneDay); const UnicodeString oneMonthStr(oneMonth); const UnicodeString oneMonthShortStr(oneMonthShort); const UnicodeString oneYearStr(oneYear); const UnicodeString oneYearShortStr(oneYearShort); const UnicodeString sevenSecondsStr(sevenSeconds); const UnicodeString sevenSecondsShortStr(sevenSecondsShort); const UnicodeString sevenMinutesStr(sevenMinutes); const UnicodeString sevenMinutesShortStr(sevenMinutesShort); const UnicodeString sevenHoursStr(sevenHours); const UnicodeString sevenHoursShortStr(sevenHoursShort); const UnicodeString sevenDaysStr(sevenDays); const UnicodeString sevenMonthsStr(sevenMonths); const UnicodeString sevenMonthsShortStr(sevenMonthsShort); const UnicodeString sevenYearsStr(sevenYears); const UnicodeString sevenYearsShortStr(sevenYearsShort); const UnicodeString expected[] = { oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr, oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr, sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr, sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr, oneSecondStr, oneMinuteStr, oneHourStr, oneDayStr, oneMonthStr, oneYearStr, oneSecondShortStr, oneMinuteShortStr, oneHourStr, oneDayStr, oneMonthShortStr, oneYearShortStr, sevenSecondsStr, sevenMinutesStr, sevenHoursStr, sevenDaysStr, sevenMonthsStr, sevenYearsStr, sevenSecondsShortStr, sevenMinutesShortStr, sevenHoursShortStr, sevenDaysStr, sevenMonthsShortStr, sevenYearsShortStr}; int counter = 0; for ( unsigned int locIndex = 0; locIndex < UPRV_LENGTHOF(locales); ++locIndex ) { Locale l = Locale::createFromName(locales[locIndex]); for ( unsigned int numberIndex = 0; numberIndex < UPRV_LENGTHOF(numbers); ++numberIndex ) { for ( unsigned int styleIndex = 0; styleIndex < UPRV_LENGTHOF(styles); ++styleIndex ) { for ( unsigned int unitIndex = 0; unitIndex < UPRV_LENGTHOF(tunits); ++unitIndex ) { TimeUnitAmount *tamt = new TimeUnitAmount(numbers[numberIndex], tunits[unitIndex], status); if (U_FAILURE(status)) { dataerrln("generating TimeUnitAmount Object failed."); #ifdef TUFMTTS_DEBUG std::cout << "Failed to get TimeUnitAmount for " << tunits[unitIndex] << "\n"; #endif return; } TimeUnitFormat *tfmt = new TimeUnitFormat(l, styles[styleIndex], status); if (U_FAILURE(status)) { dataerrln("generating TimeUnitAmount Object failed."); #ifdef TUFMTTS_DEBUG std::cout << "Failed to get TimeUnitFormat for " << locales[locIndex] << "\n"; #endif return; } Formattable fmt; UnicodeString str; fmt.adoptObject(tamt); str = ((Format *)tfmt)->format(fmt, str, status); if (!assertSuccess("formatting relative time failed", status)) { delete tfmt; #ifdef TUFMTTS_DEBUG std::cout << "Failed to format" << "\n"; #endif return; } #ifdef TUFMTTS_DEBUG char tmp[128]; //output char tmp1[128]; //expected int len = 0; u_strToUTF8(tmp, 128, &len, str.getTerminatedBuffer(), str.length(), &status); u_strToUTF8(tmp1, 128, &len, expected[counter].unescape().getTerminatedBuffer(), expected[counter].unescape().length(), &status); std::cout << "Formatted string : " << tmp << " expected : " << tmp1 << "\n"; #endif if (!assertEquals("formatted time string is not expected, locale: " + UnicodeString(locales[locIndex]) + " style: " + (int)styles[styleIndex] + " units: " + (int)tunits[unitIndex], expected[counter], str)) { delete tfmt; str.remove(); return; } delete tfmt; str.remove(); ++counter; } } } } }
void TimeUnitFormat::parseObject(const UnicodeString& source, Formattable& result, ParsePosition& pos) const { double resultNumber = -1; UBool withNumberFormat = false; TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT; int32_t oldPos = pos.getIndex(); int32_t newPos = -1; int32_t longestParseDistance = 0; UnicodeString* countOfLongestMatch = NULL; #ifdef TMUTFMT_DEBUG char res[1000]; source.extract(0, source.length(), res, "UTF-8"); std::cout << "parse source: " << res << "\n"; #endif // parse by iterating through all available patterns // and looking for the longest match. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; i = (TimeUnit::UTimeUnitFields)(i+1)) { Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i]; int32_t elemPos = -1; const UHashElement* elem = NULL; while ((elem = countToPatterns->nextElement(elemPos)) != NULL){ const UHashTok keyTok = elem->key; UnicodeString* count = (UnicodeString*)keyTok.pointer; #ifdef TMUTFMT_DEBUG count->extract(0, count->length(), res, "UTF-8"); std::cout << "parse plural count: " << res << "\n"; #endif const UHashTok valueTok = elem->value; // the value is a pair of MessageFormat* MessageFormat** patterns = (MessageFormat**)valueTok.pointer; for (EStyle style = kFull; style < kTotal; style = (EStyle)(style + 1)) { MessageFormat* pattern = patterns[style]; pos.setErrorIndex(-1); pos.setIndex(oldPos); // see if we can parse Formattable parsed; pattern->parseObject(source, parsed, pos); if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) { continue; } #ifdef TMUTFMT_DEBUG std::cout << "parsed.getType: " << parsed.getType() << "\n"; #endif double tmpNumber = 0; if (pattern->getArgTypeCount() != 0) { // pattern with Number as beginning, such as "{0} d". // check to make sure that the timeUnit is consistent Formattable& temp = parsed[0]; if (temp.getType() == Formattable::kDouble) { tmpNumber = temp.getDouble(); } else if (temp.getType() == Formattable::kLong) { tmpNumber = temp.getLong(); } else { continue; } UnicodeString select = fPluralRules->select(tmpNumber); #ifdef TMUTFMT_DEBUG select.extract(0, select.length(), res, "UTF-8"); std::cout << "parse plural select count: " << res << "\n"; #endif if (*count != select) { continue; } } int32_t parseDistance = pos.getIndex() - oldPos; if (parseDistance > longestParseDistance) { if (pattern->getArgTypeCount() != 0) { resultNumber = tmpNumber; withNumberFormat = true; } else { withNumberFormat = false; } resultTimeUnit = i; newPos = pos.getIndex(); longestParseDistance = parseDistance; countOfLongestMatch = count; } } } } /* After find the longest match, parse the number. * Result number could be null for the pattern without number pattern. * such as unit pattern in Arabic. * When result number is null, use plural rule to set the number. */ if (withNumberFormat == false && longestParseDistance != 0) { // set the number using plurrual count if ( *countOfLongestMatch == PLURAL_COUNT_ZERO ) { resultNumber = 0; } else if ( *countOfLongestMatch == PLURAL_COUNT_ONE ) { resultNumber = 1; } else if ( *countOfLongestMatch == PLURAL_COUNT_TWO ) { resultNumber = 2; } else { // should not happen. // TODO: how to handle? resultNumber = 3; } } if (longestParseDistance == 0) { pos.setIndex(oldPos); pos.setErrorIndex(0); } else { UErrorCode status = U_ZERO_ERROR; TimeUnitAmount* tmutamt = new TimeUnitAmount(resultNumber, resultTimeUnit, status); if (U_SUCCESS(status)) { result.adoptObject(tmutamt); pos.setIndex(newPos); pos.setErrorIndex(-1); } else { pos.setIndex(oldPos); pos.setErrorIndex(0); } } }