/*
*******************************************************************************
-* Copyright (C) 1997-2004, International Business Machines Corporation and others. All Rights Reserved.
+* Copyright (C) 1997-2012, International Business Machines Corporation
+* and others. All Rights Reserved.
*******************************************************************************
*/
+#include <typeinfo> // for 'typeid' to work
+
#include "unicode/rbnf.h"
#if U_HAVE_RBNF
#include "cmemory.h"
#include "cstring.h"
-#include "util.h"
+#include "patternprops.h"
+#include "uresimp.h"
// debugging
// #define DEBUG
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
-class LocalizationInfo : public UObject {
+/*
+This is a utility class. It does not use ICU's RTTI.
+If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
+Please make sure that intltest passes on Windows in Release mode,
+since the string pooling per compilation unit will mess up how RTTI works.
+The RTTI code was also removed due to lack of code coverage.
+*/
+class LocalizationInfo : public UMemory {
protected:
- virtual ~LocalizationInfo() {};
+ virtual ~LocalizationInfo();
uint32_t refcount;
public:
virtual int32_t indexForLocale(const UChar* locale) const;
virtual int32_t indexForRuleSet(const UChar* ruleset) const;
- virtual UClassID getDynamicClassID() const = 0;
- static UClassID getStaticClassID(void);
+// virtual UClassID getDynamicClassID() const = 0;
+// static UClassID getStaticClassID(void);
};
-UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
+LocalizationInfo::~LocalizationInfo() {}
+
+//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
// if both strings are NULL, this returns TRUE
static UBool
virtual const UChar* getLocaleName(int32_t index) const;
virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const;
- virtual UClassID getDynamicClassID() const;
- static UClassID getStaticClassID(void);
+// virtual UClassID getDynamicClassID() const;
+// static UClassID getStaticClassID(void);
private:
void init(UErrorCode& status) const;
void inc(void) { ++p; ch = 0xffff; }
UBool checkInc(UChar c) { if (p < e && (ch == c || *p == c)) { inc(); return TRUE; } return FALSE; }
UBool check(UChar c) { return p < e && (ch == c || *p == c); }
- void skipWhitespace(void) { while (p < e && uprv_isRuleWhiteSpace(ch != 0xffff ? ch : *p)) inc();}
+ void skipWhitespace(void) { while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) inc();}
UBool inList(UChar c, const UChar* list) const {
- if (*list == SPACE && uprv_isRuleWhiteSpace(c)) return TRUE;
+ if (*list == SPACE && PatternProps::isWhiteSpace(c)) return TRUE;
while (*list && *list != c) ++list; return *list == c;
}
void parseError(const char* msg);
}
const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;
- if (start < data)
+ if (start < data) {
start = data;
- for (UChar* x = p; --x >= start;)
+ }
+ for (UChar* x = p; --x >= start;) {
if (!*x) {
start = x+1;
break;
}
+ }
const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;
- if (limit > e)
+ if (limit > e) {
limit = e;
- u_strncpy(pe.preContext, start, p-start);
+ }
+ u_strncpy(pe.preContext, start, (int32_t)(p-start));
pe.preContext[p-start] = 0;
- u_strncpy(pe.postContext, p, limit-p);
+ u_strncpy(pe.postContext, p, (int32_t)(limit-p));
pe.postContext[limit-p] = 0;
- pe.offset = p - data;
+ pe.offset = (int32_t)(p - data);
#ifdef DEBUG
fprintf(stderr, "%s at or near character %d: ", str, p-data);
}
}
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
+//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
StringLocalizationInfo*
StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) {
const UnicodeString& locs,
const Locale& alocale, UParseError& perror, UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(alocale)
, collator(NULL)
const UnicodeString& locs,
UParseError& perror, UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(Locale::getDefault())
, collator(NULL)
LocalizationInfo* info,
const Locale& alocale, UParseError& perror, UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(alocale)
, collator(NULL)
UParseError& perror,
UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(Locale::getDefault())
, collator(NULL)
UParseError& perror,
UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(aLocale)
, collator(NULL)
RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
: ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(alocale)
, collator(NULL)
return;
}
+ const char* rules_tag = "RBNFRules";
const char* fmt_tag = "";
switch (tag) {
case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break;
case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break;
case URBNF_DURATION: fmt_tag = "DurationRules"; break;
+ case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;
default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
// TODO: read localization info from resource
LocalizationInfo* locinfo = NULL;
- int32_t len = 0;
UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);
if (U_SUCCESS(status)) {
setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),
ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));
- const UChar* description = ures_getStringByKey(nfrb, fmt_tag, &len, &status);
- UnicodeString desc(description, len);
+
+ UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status);
+ if (U_FAILURE(status)) {
+ ures_close(nfrb);
+ }
+ UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status);
+ if (U_FAILURE(status)) {
+ ures_close(rbnfRules);
+ ures_close(nfrb);
+ return;
+ }
+
+ UnicodeString desc;
+ while (ures_hasNext(ruleSets)) {
+ desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));
+ }
UParseError perror;
+
init (desc, locinfo, perror, status);
+
+ ures_close(ruleSets);
+ ures_close(rbnfRules);
}
ures_close(nfrb);
}
RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
: NumberFormat(rhs)
, ruleSets(NULL)
+ , ruleSetDescriptions(NULL)
+ , numRuleSets(0)
, defaultRuleSet(NULL)
, locale(rhs.locale)
, collator(NULL)
return TRUE;
}
- if (other.getDynamicClassID() == getStaticClassID()) {
+ if (typeid(*this) == typeid(other)) {
const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
if (locale == rhs.locale &&
lenient == rhs.lenient &&
Locale
RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const {
if (U_FAILURE(status)) {
- return Locale();
+ return Locale("");
}
if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) {
UnicodeString name(TRUE, localizations->getLocaleName(index), -1);
bp = (char *)uprv_malloc(cap);
if (bp == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
- return Locale();
+ return Locale("");
}
}
name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant);
UnicodeString& toAppendTo,
FieldPosition& /* pos */) const
{
- if (defaultRuleSet) defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+ // Special case for NaN; adapted from what DecimalFormat::_format( double number,...) does.
+ if (uprv_isNaN(number)) {
+ DecimalFormatSymbols* decFmtSyms = getDecimalFormatSymbols(); // RuleBasedNumberFormat internal
+ if (decFmtSyms) {
+ toAppendTo += decFmtSyms->getConstSymbol(DecimalFormatSymbols::kNaNSymbol);
+ }
+ } else if (defaultRuleSet) {
+ defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+ }
return toAppendTo;
}
{
// return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
if (U_SUCCESS(status)) {
- if (ruleSetName.indexOf(gPercentPercent) == 0) {
+ if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
// throw new IllegalArgumentException("Can't use internal rule set");
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
UErrorCode& status) const
{
if (U_SUCCESS(status)) {
- if (ruleSetName.indexOf(gPercentPercent) == 0) {
+ if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
// throw new IllegalArgumentException("Can't use internal rule set");
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
UErrorCode& status) const
{
if (U_SUCCESS(status)) {
- if (ruleSetName.indexOf(gPercentPercent) == 0) {
+ if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
// throw new IllegalArgumentException("Can't use internal rule set");
status = U_ILLEGAL_ARGUMENT_ERROR;
} else {
for (NFRuleSet** p = ruleSets; *p; ++p) {
NFRuleSet *rp = *p;
- if (rp->isPublic()) {
+ if (rp->isPublic() && rp->isParseable()) {
ParsePosition working_pp(0);
Formattable working_result;
- rp->parse(workingText, working_pp, kMaxDouble, working_result);
+ rp->parse(workingText, working_pp, kMaxDouble, working_result, lenient);
if (working_pp.getIndex() > high_pp.getIndex()) {
high_pp = working_pp;
high_result = working_result;
}
}
- parsePosition.setIndex(parsePosition.getIndex() + high_pp.getIndex());
+ int32_t startIndex = parsePosition.getIndex();
+ parsePosition.setIndex(startIndex + high_pp.getIndex());
if (high_pp.getIndex() > 0) {
parsePosition.setErrorIndex(-1);
+ } else {
+ int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0;
+ parsePosition.setErrorIndex(startIndex + errorIndex);
}
result = high_result;
if (result.getType() == Formattable::kDouble) {
return;
}
+ const UnicodeString spellout = UNICODE_STRING_SIMPLE("%spellout-numbering");
+ const UnicodeString ordinal = UNICODE_STRING_SIMPLE("%digits-ordinal");
+ const UnicodeString duration = UNICODE_STRING_SIMPLE("%duration");
+
NFRuleSet**p = &ruleSets[0];
while (*p) {
- ++p;
+ if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) {
+ defaultRuleSet = *p;
+ return;
+ } else {
+ ++p;
+ }
}
defaultRuleSet = *--p;
void
RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,
- UParseError& /* pErr */, UErrorCode& status)
+ UParseError& pErr, UErrorCode& status)
{
// TODO: implement UParseError
+ uprv_memset(&pErr, 0, sizeof(UParseError));
// Note: this can leave ruleSets == NULL, so remaining code should check
if (U_FAILURE(status)) {
return;
// is, pull them out into our temporary holding place for them,
// and delete them from the description before the real desciption-
// parsing code sees them
- int32_t lp = description.indexOf(gLenientParse);
+ int32_t lp = description.indexOf(gLenientParse, -1, 0);
if (lp != -1) {
// we've got to make sure we're not in the middle of a rule
// (where "%%lenient-parse" would actually get treated as
// locate the beginning and end of the actual collation
// rules (there may be whitespace between the name and
// the first token in the description)
- int lpEnd = description.indexOf(gSemiPercent, lp);
+ int lpEnd = description.indexOf(gSemiPercent, 2, lp);
if (lpEnd == -1) {
lpEnd = description.length() - 1;
}
int lpStart = lp + u_strlen(gLenientParse);
- while (uprv_isRuleWhiteSpace(description.charAt(lpStart))) {
+ while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
++lpStart;
}
// pre-flight parsing the description and count the number of
// rule sets (";%" marks the end of one rule set and the beginning
// of the next)
- int numRuleSets = 0;
- for (int32_t p = description.indexOf(gSemiPercent); p != -1; p = description.indexOf(gSemiPercent, p)) {
+ numRuleSets = 0;
+ for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) {
++numRuleSets;
++p;
}
// because we have to know the names and locations of all the rule
// sets before we can actually set everything up
if(!numRuleSets) {
- status = U_ILLEGAL_ARGUMENT_ERROR;
- return;
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
}
- UnicodeString* ruleSetDescriptions = new UnicodeString[numRuleSets];
- /* test for NULL */
+
+ ruleSetDescriptions = new UnicodeString[numRuleSets];
if (ruleSetDescriptions == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
{
int curRuleSet = 0;
int32_t start = 0;
- for (int32_t p = description.indexOf(gSemiPercent); p != -1; p = description.indexOf(gSemiPercent, start)) {
+ for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) {
ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);
ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
- /* test for NULL */
if (ruleSets[curRuleSet] == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
- /* test for NULL */
if (ruleSets[curRuleSet] == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
}
- delete[] ruleSetDescriptions;
-
// Now that the rules are initialized, the 'real' default rule
// set can be adjusted by the localization data.
while (start != -1 && start < description.length()) {
// seek to the first non-whitespace character...
while (start < description.length()
- && uprv_isRuleWhiteSpace(description.charAt(start))) {
+ && PatternProps::isWhiteSpace(description.charAt(start))) {
++start;
}
ruleSets = NULL;
}
+ if (ruleSetDescriptions) {
+ delete [] ruleSetDescriptions;
+ }
+
#if !UCONFIG_NO_COLLATION
delete collator;
#endif
UErrorCode status = U_ZERO_ERROR;
Collator* temp = Collator::createInstance(locale, status);
- if (U_SUCCESS(status) &&
- temp->getDynamicClassID() == RuleBasedCollator::getStaticClassID()) {
-
- RuleBasedCollator* newCollator = (RuleBasedCollator*)temp;
+ RuleBasedCollator* newCollator;
+ if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) {
if (lenientParseRules) {
UnicodeString rules(newCollator->getRules());
rules.append(*lenientParseRules);
newCollator = new RuleBasedCollator(rules, status);
+ // Exit if newCollator could not be created.
+ if (newCollator == NULL) {
+ return NULL;
+ }
} else {
temp = NULL;
}
return decimalFormatSymbols;
}
+// De-owning the current localized symbols and adopt the new symbols.
+void
+RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt)
+{
+ if (symbolsToAdopt == NULL) {
+ return; // do not allow caller to set decimalFormatSymbols to NULL
+ }
+
+ if (decimalFormatSymbols != NULL) {
+ delete decimalFormatSymbols;
+ }
+
+ decimalFormatSymbols = symbolsToAdopt;
+
+ {
+ // Apply the new decimalFormatSymbols by reparsing the rulesets
+ UErrorCode status = U_ZERO_ERROR;
+
+ for (int32_t i = 0; i < numRuleSets; i++) {
+ ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status);
+ }
+ }
+}
+
+// Setting the symbols is equlivalent to adopting a newly created localized symbols.
+void
+RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
+{
+ adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
+}
+
U_NAMESPACE_END
/* U_HAVE_RBNF */