/* 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();
uint32_t refcount;
/* * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status, * and return nullptr. The StringLocalizationInfo will adopt locData if it is created.
*/
StringLocalizationInfo* parse(char16_t* data, int32_t len);
private:
inlinevoid inc() {
++p;
ch = 0xffff;
} inline UBool checkInc(char16_t c) { if (p < e && (ch == c || *p == c)) {
inc(); returntrue;
} returnfalse;
} inline UBool check(char16_t c) { return p < e && (ch == c || *p == c);
} inlinevoid skipWhitespace() { while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) {
inc();
}
} inline UBool inList(char16_t c, const char16_t* list) const { if (*list == SPACE && PatternProps::isWhiteSpace(c)) { returntrue;
} while (*list && *list != c) {
++list;
} return *list == c;
} void parseError(constchar* msg);
skipWhitespace(); if (!checkInc(CLOSE_ANGLE)) { if (check(OPEN_ANGLE)) {
ERROR("Missing comma in outer array");
} else {
ERROR("Missing close angle bracket in outer array");
}
}
skipWhitespace(); if (p != e) {
ERROR("Extra text after close of localization data");
}
array.add(nullptr, ec); if (U_SUCCESS(ec)) {
int32_t numLocs = array.length() - 2; // subtract first, nullptr
char16_t*** result = reinterpret_cast<char16_t***>(array.release());
char16_t*
LocDataParser::nextString() {
char16_t* result = nullptr;
skipWhitespace(); if (p < e) { const char16_t* terminators;
char16_t c = *p;
UBool haveQuote = c == QUOTE || c == TICK; if (haveQuote) {
inc();
terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST;
} else {
terminators = NOQUOTE_STOPLIST;
}
char16_t* start = p; while (p < e && !inList(*p, terminators)) ++p; if (p == e) {
ERROR("Unexpected end of data");
}
char16_t x = *p; if (p > start) {
ch = x;
*p = 0x0; // terminate by writing to data
result = start; // just point into data
} if (haveQuote) { if (x != c) {
ERROR("Missing matching quote");
} elseif (p == start) {
ERROR("Empty string");
}
inc();
} elseif (x == OPEN_ANGLE || x == TICK || x == QUOTE) {
ERROR("Unexpected character in string");
}
}
// ok for there to be no next string return result;
}
void LocDataParser::parseError(constchar* EXPLANATION_ARG)
{ if (!data) { return;
}
int32_t len = info.length(); if (len == 0) { return nullptr; // no error;
}
char16_t* p = static_cast<char16_t*>(uprv_malloc(len * sizeof(char16_t))); if (!p) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
info.extract(p, len, status); if (!U_FAILURE(status)) {
status = U_ZERO_ERROR; // clear warning about non-termination
}
StringLocalizationInfo::~StringLocalizationInfo() { for (char16_t*** p = data; *p; ++p) { // remaining data is simply pointer into our unicode string data. if (*p) uprv_free(*p);
} if (data) uprv_free(data); if (info) uprv_free(info);
}
if (typeid(*this) == typeid(other)) { const RuleBasedNumberFormat& rhs = static_cast<const RuleBasedNumberFormat&>(other); // test for capitalization info equality is adequately handled // by the NumberFormat test for fCapitalizationContext equality; // the info here is just derived from that. if (locale == rhs.locale &&
lenient == rhs.lenient &&
(localizations == nullptr
? rhs.localizations == nullptr
: (rhs.localizations == nullptr
? false
: *localizations == rhs.localizations))) {
NFRuleSet*
RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const
{ if (U_SUCCESS(status) && fRuleSets) { for (NFRuleSet** p = fRuleSets; *p; ++p) {
NFRuleSet* rs = *p; if (rs->isNamed(name)) { return rs;
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
} return nullptr;
}
UnicodeString&
RuleBasedNumberFormat::format(const DecimalQuantity &number,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode &status) const { if (U_FAILURE(status)) { return appendTo;
}
DecimalQuantity copy(number); if (copy.fitsInLong()) {
format(number.toLong(), appendTo, pos, status);
} else {
copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status); if (copy.fitsInLong()) {
format(number.toDouble(), appendTo, pos, status);
} else { // We're outside of our normal range that this framework can handle. // The DecimalFormat will provide more accurate results.
// TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status); if (decimalFormat.isNull()) { return appendTo;
}
Formattable f;
LocalPointer<DecimalQuantity> decimalQuantity(new DecimalQuantity(number), status); if (decimalQuantity.isNull()) { return appendTo;
}
f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity.
decimalFormat->format(f, appendTo, pos, status);
}
} return appendTo;
}
/** * Bottleneck through which all the public format() methods * that take a long pass. By the time we get here, we know * which rule set we're using to do the formatting. * @param number The number to format * @param ruleSet The rule set to use to format the number * @return The text that resulted from formatting the number
*/
UnicodeString&
RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const
{ // all API format() routines that take a double vector through // here. We have these two identical functions-- one taking a // double and one taking a long-- the couple digits of precision // that long has but double doesn't (both types are 8 bytes long, // but double has to borrow some of the mantissa bits to hold // the exponent). // Create an empty string buffer where the result will // be built, and pass it to the rule set (along with an insertion // position of 0 and the number being formatted) to the rule set // for formatting
if (U_SUCCESS(status)) { if (number == U_INT64_MIN) { // We can't handle this value right now. Provide an accurate default value.
// TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); if (decimalFormat == nullptr) { return toAppendTo;
}
Formattable f;
FieldPosition pos(FieldPosition::DONT_CARE);
DecimalQuantity *decimalQuantity = new DecimalQuantity(); if (decimalQuantity == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; delete decimalFormat; return toAppendTo;
}
decimalQuantity->setToLong(number);
f.adoptDecimalQuantity(decimalQuantity); // f now owns decimalQuantity.
decimalFormat->format(f, toAppendTo, pos, status); delete decimalFormat;
} else {
int32_t startPos = toAppendTo.length();
ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
adjustForCapitalizationContext(startPos, toAppendTo, status);
}
} return toAppendTo;
}
UnicodeString&
RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos,
UnicodeString& currentResult,
UErrorCode& status) const
{ #if !UCONFIG_NO_BREAK_ITERATION
UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) { // capitalize currentResult according to context
UChar32 ch = currentResult.char32At(0); if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != nullptr &&
( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
(capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
(capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) { // titlecase first word of currentResult, here use sentence iterator unlike current implementations // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
}
} #endif return currentResult;
}
if (high_pp.getIndex() == workingText.length()) { break;
}
}
}
}
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) { double d = result.getDouble(); if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) { // Note: casting a double to an int when the double is too large or small // to fit the destination is undefined behavior. The explicit range checks, // above, are required. Just casting and checking the result value is undefined.
result.setLong(static_cast<int32_t>(d));
}
}
}
UnicodeString description(rules); if (!description.length()) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
// start by stripping the trailing whitespace from all the rules // (this is all the whitespace following each semicolon in the // description). This allows us to look for rule-set boundaries // by searching for ";%" without having to worry about whitespace // between the ; and the %
stripWhitespace(description);
// check to see if there's a set of lenient-parse rules. If there // 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, -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 // rule text) if (lp == 0 || description.charAt(lp - 1) == gSemiColon) { // 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, 2, lp);
if (lpEnd == -1) {
lpEnd = description.length() - 1;
} int lpStart = lp + u_strlen(gLenientParse); while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
++lpStart;
}
// copy out the lenient-parse rules and delete them // from the description
lenientParseRules = new UnicodeString(); /* test for nullptr */ if (lenientParseRules == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
lenientParseRules->setTo(description, lpStart, lpEnd - lpStart);
description.remove(lp, lpEnd + 1 - lp);
}
}
// 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)
numRuleSets = 0; for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) {
++numRuleSets;
++p;
}
++numRuleSets;
// our rule list is an array of the appropriate size
fRuleSets = static_cast<NFRuleSet**>(uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet*))); /* test for nullptr */ if (fRuleSets == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
for (int i = 0; i <= numRuleSets; ++i) {
fRuleSets[i] = nullptr;
}
// divide up the descriptions into individual rule-set descriptions // and store them in a temporary array. At each step, we also // new up a rule set, but all this does is initialize its name // and remove it from its description. We can't actually parse // the rest of the descriptions and finish initializing everything // 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;
}
ruleSetDescriptions = new UnicodeString[numRuleSets]; if (ruleSetDescriptions == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
{ int curRuleSet = 0;
int32_t start = 0; 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);
fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); if (fRuleSets[curRuleSet] == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
++curRuleSet;
start = p + 1;
}
ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status); if (fRuleSets[curRuleSet] == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
}
// now we can take note of the formatter's default rule set, which // is the last public rule set in the description (it's the last // rather than the first so that a user can create a new formatter // from an existing formatter and change its default behavior just // by appending more rule sets to the end)
// {dlf} Initialization of a fraction rule set requires the default rule // set to be known. For purposes of initialization, this is always the // last public rule set, no matter what the localization data says.
initDefaultRuleSet();
// finally, we can go back through the temporary descriptions // list and finish setting up the substructure (and we throw // away the temporary descriptions as we go)
{ for (int i = 0; i < numRuleSets; i++) {
fRuleSets[i]->parseRules(ruleSetDescriptions[i], status);
}
}
// Now that the rules are initialized, the 'real' default rule // set can be adjusted by the localization data.
// The C code keeps the localization array as is, rather than building // a separate array of the public rule set names, so we have less work // to do here-- but we still need to check the names.
if (localizationInfos) { // confirm the names, if any aren't in the rules, that's an error // it is ok if the rules contain public rule sets that are not in this list for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) {
UnicodeString name(true, localizationInfos->getRuleSetName(i), -1);
NFRuleSet* rs = findRuleSet(name, status); if (rs == nullptr) { break; // error
} if (i == 0) {
defaultRuleSet = rs;
}
}
} else {
defaultRuleSet = getDefaultRuleSet();
}
originalDescription = rules;
}
// override the NumberFormat implementation in order to // lazily initialize relevant items void
RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status)
{
NumberFormat::setContext(value, status); if (U_SUCCESS(status)) { if (!capitalizationInfoSet &&
(value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) {
initCapitalizationContextInfo(locale);
capitalizationInfoSet = true;
} #if !UCONFIG_NO_BREAK_ITERATION if ( capitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
(value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
(value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) {
status = U_ZERO_ERROR;
capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status); if (U_FAILURE(status)) { delete capitalizationBrkIter;
capitalizationBrkIter = nullptr;
}
} #endif
}
}
void
RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
{ // iterate through the characters...
UnicodeString result;
int start = 0; while (start != -1 && start < description.length()) { // seek to the first non-whitespace character... while (start < description.length()
&& PatternProps::isWhiteSpace(description.charAt(start))) {
++start;
}
// locate the next semicolon in the text and copy the text from // our current position up to that semicolon into the result
int32_t p = description.indexOf(gSemiColon, start); if (p == -1) { // or if we don't find a semicolon, just copy the rest of // the string into the result
result.append(description, start, description.length() - start);
start = -1;
} elseif (p < description.length()) {
result.append(description, start, p + 1 - start);
start = p + 1;
}
// when we get here, we've seeked off the end of the string, and // we terminate the loop (we continue until *start* is -1 rather // than until *p* is -1, because otherwise we'd miss the last // rule in the description) else {
start = -1;
}
}
description.setTo(result);
}
void
RuleBasedNumberFormat::dispose()
{ if (fRuleSets) { for (NFRuleSet** p = fRuleSets; *p; ++p) { delete *p;
}
uprv_free(fRuleSets);
fRuleSets = nullptr;
}
if (ruleSetDescriptions) { delete [] ruleSetDescriptions;
ruleSetDescriptions = nullptr;
}
if (localizations) {
localizations = localizations->unref();
}
}
//----------------------------------------------------------------------- // package-internal API //-----------------------------------------------------------------------
/** * Returns the collator to use for lenient parsing. The collator is lazily created: * this function creates it the first time it's called. * @return The collator to use for lenient parsing, or null if lenient parsing * is turned off.
*/ const RuleBasedCollator*
RuleBasedNumberFormat::getCollator() const
{ #if !UCONFIG_NO_COLLATION if (!fRuleSets) { return nullptr;
}
// lazy-evaluate the collator if (collator == nullptr && lenient) { // create a default collator based on the formatter's locale, // then pull out that collator's rules, append any additional // rules specified in the description, and create a _new_ // collator based on the combination of those rules
newCollator = new RuleBasedCollator(rules, status); // Exit if newCollator could not be created. if (newCollator == nullptr) { return nullptr;
}
} else {
temp = nullptr;
} if (U_SUCCESS(status)) {
newCollator->setAttribute(UCOL_DECOMPOSITION_MODE, UCOL_ON, status); // cast away const const_cast<RuleBasedNumberFormat*>(this)->collator = newCollator;
} else { delete newCollator;
}
} delete temp;
} #endif
// if lenient-parse mode is off, this will be null // (see setLenientParseMode()) return collator;
}
DecimalFormatSymbols*
RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)
{ // lazy-evaluate the DecimalFormatSymbols object. This object // is shared by all DecimalFormat instances belonging to this // formatter if (decimalFormatSymbols == nullptr) {
LocalPointer<DecimalFormatSymbols> temp(new DecimalFormatSymbols(locale, status), status); if (U_SUCCESS(status)) {
decimalFormatSymbols = temp.orphan();
}
} return decimalFormatSymbols;
}
/** * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat * instances owned by this formatter.
*/ const DecimalFormatSymbols*
RuleBasedNumberFormat::getDecimalFormatSymbols() const
{ return decimalFormatSymbols;
}
// De-owning the current localized symbols and adopt the new symbols. void
RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt)
{ if (symbolsToAdopt == nullptr) { return; // do not allow caller to set decimalFormatSymbols to nullptr
}
{ // Apply the new decimalFormatSymbols by reparsing the rulesets
UErrorCode status = U_ZERO_ERROR;
delete defaultInfinityRule;
defaultInfinityRule = nullptr;
initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols
delete defaultNaNRule;
defaultNaNRule = nullptr;
initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols
if (fRuleSets) { for (int32_t i = 0; i < numRuleSets; i++) {
fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
}
}
}
}
// Setting the symbols is equivalent to adopting a newly created localized symbols. void
RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
{
adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
}
PluralFormat *
RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType, const UnicodeString &pattern,
UErrorCode& status) const
{ auto *pf = new PluralFormat(locale, pluralType, pattern, status); if (pf == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} return pf;
}
/** * Get the rounding mode. * @return A rounding mode
*/
DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const { return fRoundingMode;
}
/** * Set the rounding mode. This has no effect unless the rounding * increment is greater than zero. * @param roundingMode A rounding mode
*/ void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) {
fRoundingMode = roundingMode;
}
U_NAMESPACE_END
/* U_HAVE_RBNF */ #endif
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.