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;
}
/** *Bottleneckthroughwhichallthepublicformat()methods *thattakealongpass.Bythetimewegethere,weknow *whichrulesetwe'reusingtodotheformatting. *@paramnumberThenumbertoformat *@paramruleSetTherulesettousetoformatthenumber *@returnThetextthatresultedfromformattingthenumber
*/
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 // create 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;
} if (U_FAILURE(status)) { 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;
} if (U_FAILURE(status)) { 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();
// Now that we know all the rule names, 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); if (U_FAILURE(status)) { return;
}
}
}
// 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 (U_FAILURE(status)) { return;
} 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;
UChar ch; while (start < description.length()) { // Seek to the first non-whitespace character... // If the first non-whitespace character is semicolon, skip it and continue while (start < description.length()
&& (PatternProps::isWhiteSpace(ch = description.charAt(start)) || ch == gSemiColon))
{
++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); break;
} elseif (p < description.length()) {
result.append(description, start, p + 1 - start);
start = p + 1;
} // when we get here from the else, 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)
}
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 //-----------------------------------------------------------------------
// 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;
}
// 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;
}
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.