/** * Convert an integer value to a string and append the result to * the given UnicodeString.
*/ static UnicodeString& itos(int32_t i, UnicodeString& appendTo) {
char16_t temp[16];
uprv_itou(temp,16,i,10,0); // 10 == radix
appendTo.append(temp, -1); return appendTo;
}
// AppendableWrapper: encapsulates the result of formatting, keeping track // of the string and its length. class AppendableWrapper : public UMemory { public:
AppendableWrapper(Appendable& appendable) : app(appendable), len(0) {
} void append(const UnicodeString& s) {
app.appendString(s.getBuffer(), s.length());
len += s.length();
} void append(const char16_t* s, const int32_t sLength) {
app.appendString(s, sLength);
len += sLength;
} void append(const UnicodeString& s, int32_t start, int32_t length) {
append(s.tempSubString(start, length));
} void formatAndAppend(const Format* formatter, const Formattable& arg, UErrorCode& ec) {
UnicodeString s;
formatter->format(arg, s, ec); if (U_SUCCESS(ec)) {
append(s);
}
} void formatAndAppend(const Format* formatter, const Formattable& arg, const UnicodeString &argString, UErrorCode& ec) { if (!argString.isEmpty()) { if (U_SUCCESS(ec)) {
append(argString);
}
} else {
formatAndAppend(formatter, arg, ec);
}
}
int32_t length() { return len;
} private:
Appendable& app;
int32_t len;
};
// ------------------------------------- // Creates a MessageFormat instance based on the pattern.
/** * Allocate argTypes[] to at least the given capacity and return * true if successful. If not, leave argTypes[] unchanged. * * If argTypes is nullptr, allocate it. If it is not nullptr, enlarge it * if necessary to be at least as large as specified.
*/
UBool MessageFormat::allocateArgTypes(int32_t capacity, UErrorCode& status) { if (U_FAILURE(status)) { returnfalse;
} if (argTypeCapacity >= capacity) { returntrue;
} if (capacity < DEFAULT_INITIAL_CAPACITY) {
capacity = DEFAULT_INITIAL_CAPACITY;
} elseif (capacity < 2*argTypeCapacity) {
capacity = 2*argTypeCapacity;
}
Formattable::Type* a = static_cast<Formattable::Type*>(
uprv_realloc(argTypes, sizeof(*argTypes) * capacity)); if (a == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; returnfalse;
}
argTypes = a;
argTypeCapacity = capacity; returntrue;
}
const MessageFormat&
MessageFormat::operator=(const MessageFormat& that)
{ if (this != &that) { // Calls the super class for assignment first.
Format::operator=(that);
// ------------------------------------- // Applies the new pattern and returns an error if the pattern // is not correct. void
MessageFormat::applyPattern(const UnicodeString& pattern,
UParseError& parseError,
UErrorCode& ec)
{ if(U_FAILURE(ec)) { return;
}
msgPattern.parse(pattern, &parseError, ec);
cacheExplicitFormats(ec);
// Sets a custom formatter for a MessagePattern ARG_START part index. // "Custom" formatters are provided by the user via setFormat() or similar APIs. void MessageFormat::setCustomArgStartFormat(int32_t argStart,
Format* formatter,
UErrorCode& status) {
setArgStartFormat(argStart, formatter, status); if (customFormatArgStarts == nullptr) {
customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong,
nullptr, &status);
}
uhash_iputi(customFormatArgStarts, argStart, 1, &status);
}
Format* MessageFormat::getCachedFormatter(int32_t argumentNumber) const { if (cachedFormatters == nullptr) { return nullptr;
} void* ptr = uhash_iget(cachedFormatters, argumentNumber); if (ptr != nullptr && dynamic_cast<DummyFormat*>(static_cast<Format*>(ptr)) == nullptr) { returnstatic_cast<Format*>(ptr);
} else { // Not cached, or a DummyFormat representing setFormat(nullptr). return nullptr;
}
}
// ------------------------------------- // Adopts the new formats array and updates the array count. // This MessageFormat instance owns the new formats. void
MessageFormat::adoptFormats(Format** newFormats,
int32_t count) { if (newFormats == nullptr || count < 0) { return;
} // Throw away any cached formatters. if (cachedFormatters != nullptr) {
uhash_removeAll(cachedFormatters);
} if (customFormatArgStarts != nullptr) {
uhash_removeAll(customFormatArgStarts);
}
int32_t formatNumber = 0;
UErrorCode status = U_ZERO_ERROR; for (int32_t partIndex = 0;
formatNumber < count && U_SUCCESS(status) &&
(partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
setCustomArgStartFormat(partIndex, newFormats[formatNumber], status);
++formatNumber;
} // Delete those that didn't get used (if any). for (; formatNumber < count; ++formatNumber) { delete newFormats[formatNumber];
}
}
// ------------------------------------- // Sets the new formats array and updates the array count. // This MessageFormat instance makes a copy of the new formats.
void
MessageFormat::setFormats(const Format** newFormats,
int32_t count) { if (newFormats == nullptr || count < 0) { return;
} // Throw away any cached formatters. if (cachedFormatters != nullptr) {
uhash_removeAll(cachedFormatters);
} if (customFormatArgStarts != nullptr) {
uhash_removeAll(customFormatArgStarts);
}
UErrorCode status = U_ZERO_ERROR;
int32_t formatNumber = 0; for (int32_t partIndex = 0;
formatNumber < count && U_SUCCESS(status) && (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
Format* newFormat = nullptr; if (newFormats[formatNumber] != nullptr) {
newFormat = newFormats[formatNumber]->clone(); if (newFormat == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
}
}
setCustomArgStartFormat(partIndex, newFormat, status);
++formatNumber;
} if (U_FAILURE(status)) {
resetPattern();
}
}
// ------------------------------------- // Adopt a single format by format number. // Do nothing if the format number is not less than the array count.
void
MessageFormat::adoptFormat(int32_t n, Format *newFormat) {
LocalPointer<Format> p(newFormat); if (n >= 0) {
int32_t formatNumber = 0; for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { if (n == formatNumber) {
UErrorCode status = U_ZERO_ERROR;
setCustomArgStartFormat(partIndex, p.orphan(), status); return;
}
++formatNumber;
}
}
}
// ------------------------------------- // Adopt a single format by format name. // Do nothing if there is no match of formatName. void
MessageFormat::adoptFormat(const UnicodeString& formatName,
Format* formatToAdopt,
UErrorCode& status) {
LocalPointer<Format> p(formatToAdopt); if (U_FAILURE(status)) { return;
}
int32_t argNumber = MessagePattern::validateArgumentName(formatName); if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
} for (int32_t partIndex = 0;
(partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
) { if (argNameMatches(partIndex + 1, formatName, argNumber)) {
Format* f; if (p.isValid()) {
f = p.orphan();
} elseif (formatToAdopt == nullptr) {
f = nullptr;
} else {
f = formatToAdopt->clone(); if (f == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
}
setCustomArgStartFormat(partIndex, f, status);
}
}
}
// ------------------------------------- // Set a single format. // Do nothing if the variable is not less than the array count. void
MessageFormat::setFormat(int32_t n, const Format& newFormat) {
if (n >= 0) {
int32_t formatNumber = 0; for (int32_t partIndex = 0;
(partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { if (n == formatNumber) {
Format* new_format = newFormat.clone(); if (new_format) {
UErrorCode status = U_ZERO_ERROR;
setCustomArgStartFormat(partIndex, new_format, status);
} return;
}
++formatNumber;
}
}
}
// ------------------------------------- // Get a single format by format name. // Do nothing if the variable is not less than the array count.
Format *
MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) { if (U_FAILURE(status) || cachedFormatters == nullptr) return nullptr;
int32_t argNumber = MessagePattern::validateArgumentName(formatName); if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
} for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { if (argNameMatches(partIndex + 1, formatName, argNumber)) { return getCachedFormatter(partIndex);
}
} return nullptr;
}
// ------------------------------------- // Set a single format by format name // Do nothing if the variable is not less than the array count. void
MessageFormat::setFormat(const UnicodeString& formatName, const Format& newFormat,
UErrorCode& status) { if (U_FAILURE(status)) return;
int32_t argNumber = MessagePattern::validateArgumentName(formatName); if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
status = U_ILLEGAL_ARGUMENT_ERROR; return;
} for (int32_t partIndex = 0;
(partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
) { if (argNameMatches(partIndex + 1, formatName, argNumber)) {
Format* new_format = newFormat.clone(); if (new_format == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
setCustomArgStartFormat(partIndex, new_format, status);
}
}
}
// ------------------------------------- // Gets the format array. const Format**
MessageFormat::getFormats(int32_t& cnt) const
{ // This old API returns an array (which we hold) of Format* // pointers. The array is valid up to the next call to any // method on this object. We construct and resize an array // on demand that contains aliases to the subformats[i].format // pointers.
// Get total required capacity first (it's refreshed on each call).
int32_t totalCapacity = 0; for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {}
MessageFormat* t = const_cast<MessageFormat*> (this);
cnt = 0; if (formatAliases == nullptr) {
t->formatAliasesCapacity = totalCapacity;
Format** a = static_cast<Format**>(
uprv_malloc(sizeof(Format*) * formatAliasesCapacity)); if (a == nullptr) {
t->formatAliasesCapacity = 0; return nullptr;
}
t->formatAliases = a;
} elseif (totalCapacity > formatAliasesCapacity) {
Format** a = static_cast<Format**>(
uprv_realloc(formatAliases, sizeof(Format*) * totalCapacity)); if (a == nullptr) {
t->formatAliasesCapacity = 0; return nullptr;
}
t->formatAliases = a;
t->formatAliasesCapacity = totalCapacity;
}
// ------------------------------------- // Formats the source Formattable array and copy into the result buffer. // Ignore the FieldPosition result for error checking.
// ------------------------------------- // Internally creates a MessageFormat instance based on the // pattern and formats the arguments Formattable array and // copy into the appendTo buffer.
// ------------------------------------- // Formats the source Formattable object and copy into the // appendTo buffer. The Formattable object must be an array // of Formattable instances, returns error otherwise.
// Does linear search to find the match for an ArgName. const Formattable* MessageFormat::getArgFromListByName(const Formattable* arguments, const UnicodeString *argumentNames,
int32_t cnt, UnicodeString& name) const { for (int32_t i = 0; i < cnt; ++i) { if (0 == argumentNames[i].compare(name)) { return arguments + i;
}
} return nullptr;
}
/** * Mutable input/output values for the PluralSelectorProvider. * Separate so that it is possible to make MessageFormat Freezable.
*/ class PluralSelectorContext { public:
PluralSelectorContext(int32_t start, const UnicodeString &name, const Formattable &num, double off, UErrorCode &errorCode)
: startIndex(start), argName(name), offset(off),
numberArgIndex(-1), formatter(nullptr), forReplaceNumber(false) { // number needs to be set even when select() is not called. // Keep it as a Number/Formattable: // For format() methods, and to preserve information (e.g., BigDecimal). if(off == 0) {
number = num;
} else {
number = num.getDouble(errorCode) - off;
}
}
// Input values for plural selection with decimals.
int32_t startIndex; const UnicodeString &argName; /** argument number - plural offset */
Formattable number; double offset; // Output values for plural selection with decimals. /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */
int32_t numberArgIndex; const Format *formatter; /** formatted argument number - plural offset */
UnicodeString numberString; /** true if number-offset was formatted with the stock number formatter */
UBool forReplaceNumber;
};
} // namespace
// if argumentNames is nullptr, this means arguments is a numeric array. // arguments can not be nullptr. // We use const void *plNumber rather than const PluralSelectorContext *pluralNumber // so that we need not declare the PluralSelectorContext in the public header file. void MessageFormat::format(int32_t msgStart, constvoid *plNumber, const Formattable* arguments, const UnicodeString *argumentNames,
int32_t cnt,
AppendableWrapper& appendTo,
FieldPosition* ignore,
UErrorCode& success) const { if (U_FAILURE(success)) { return;
}
const UnicodeString& msgString = msgPattern.getPatternString();
int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); for (int32_t i = msgStart + 1; U_SUCCESS(success) ; ++i) { const MessagePattern::Part* part = &msgPattern.getPart(i); const UMessagePatternPartType type = part->getType();
int32_t index = part->getIndex();
appendTo.append(msgString, prevIndex, index - prevIndex); if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { return;
}
prevIndex = part->getLimit(); if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { const PluralSelectorContext &pluralNumber =
*static_cast<const PluralSelectorContext *>(plNumber); if(pluralNumber.forReplaceNumber) { // number-offset was already formatted.
appendTo.formatAndAppend(pluralNumber.formatter,
pluralNumber.number, pluralNumber.numberString, success);
} else { const NumberFormat* nf = getDefaultNumberFormat(success);
appendTo.formatAndAppend(nf, pluralNumber.number, success);
} continue;
} if (type != UMSGPAT_PART_TYPE_ARG_START) { continue;
}
int32_t argLimit = msgPattern.getLimitPartIndex(i);
UMessagePatternArgType argType = part->getArgType();
part = &msgPattern.getPart(++i); const Formattable* arg;
UBool noArg = false;
UnicodeString argName = msgPattern.getSubstring(*part); if (argumentNames == nullptr) {
int32_t argNumber = part->getValue(); // ARG_NUMBER if (0 <= argNumber && argNumber < cnt) {
arg = arguments + argNumber;
} else {
arg = nullptr;
noArg = true;
}
} else {
arg = getArgFromListByName(arguments, argumentNames, cnt, argName); if (arg == nullptr) {
noArg = true;
}
}
++i;
int32_t prevDestLength = appendTo.length(); const Format* formatter = nullptr; if (noArg) {
appendTo.append(
UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE));
} elseif (arg == nullptr) {
appendTo.append(NULL_STRING, 4);
} elseif(plNumber!=nullptr && static_cast<const PluralSelectorContext *>(plNumber)->numberArgIndex==(i-2)) { const PluralSelectorContext &pluralNumber =
*static_cast<const PluralSelectorContext *>(plNumber); if(pluralNumber.offset == 0) { // The number was already formatted with this formatter.
appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number,
pluralNumber.numberString, success);
} else { // Do not use the formatted (number-offset) string for a named argument // that formats the number without subtracting the offset.
appendTo.formatAndAppend(pluralNumber.formatter, *arg, success);
}
} elseif ((formatter = getCachedFormatter(i - 2)) != nullptr) { // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. if (dynamic_cast<const ChoiceFormat*>(formatter) || dynamic_cast<const PluralFormat*>(formatter) || dynamic_cast<const SelectFormat*>(formatter)) { // We only handle nested formats here if they were provided via // setFormat() or its siblings. Otherwise they are not cached and instead // handled below according to argType.
UnicodeString subMsgString;
formatter->format(*arg, subMsgString, success); if (subMsgString.indexOf(LEFT_CURLY_BRACE) >= 0 ||
(subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern))
) {
MessageFormat subMsgFormat(subMsgString, fLocale, success);
subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, ignore, success);
} else {
appendTo.append(subMsgString);
}
} else {
appendTo.formatAndAppend(formatter, *arg, success);
}
} elseif (argType == UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i - 2))) { // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table. // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check // for the hash table containing DummyFormat. if (arg->isNumeric()) { const NumberFormat* nf = getDefaultNumberFormat(success);
appendTo.formatAndAppend(nf, *arg, success);
} elseif (arg->getType() == Formattable::kDate) { const DateFormat* df = getDefaultDateFormat(success);
appendTo.formatAndAppend(df, *arg, success);
} else {
appendTo.append(arg->getString(success));
}
} elseif (argType == UMSGPAT_ARG_TYPE_CHOICE) { if (!arg->isNumeric()) {
success = U_ILLEGAL_ARGUMENT_ERROR; return;
} // We must use the Formattable::getDouble() variant with the UErrorCode parameter // because only this one converts non-double numeric types to double. constdouble number = arg->getDouble(success);
int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number);
formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames,
cnt, appendTo, success);
} elseif (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) { if (!arg->isNumeric()) {
success = U_ILLEGAL_ARGUMENT_ERROR; return;
} const PluralSelectorProvider &selector =
argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider; // We must use the Formattable::getDouble() variant with the UErrorCode parameter // because only this one converts non-double numeric types to double. double offset = msgPattern.getPluralOffset(i);
PluralSelectorContext context(i, argName, *arg, offset, success);
int32_t subMsgStart = PluralFormat::findSubMessage(
msgPattern, i, selector, &context, arg->getDouble(success), success);
formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames,
cnt, appendTo, success);
} elseif (argType == UMSGPAT_ARG_TYPE_SELECT) {
int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success);
formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames,
cnt, appendTo, success);
} else { // This should never happen.
success = U_INTERNAL_PROGRAM_ERROR; return;
}
ignore = updateMetaData(appendTo, prevDestLength, ignore, arg);
prevIndex = msgPattern.getPart(argLimit).getLimit();
i = argLimit;
}
}
// JDK compatibility mode: (see JDK MessageFormat.format() API docs) // - remove SKIP_SYNTAX; that is, remove half of the apostrophes // - if the result string contains an open curly brace '{' then // instantiate a temporary MessageFormat object and format again; // otherwise just append the result string const UnicodeString& msgString = msgPattern.getPatternString();
UnicodeString sb;
int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); for (int32_t i = msgStart;;) { const MessagePattern::Part& part = msgPattern.getPart(++i); const UMessagePatternPartType type = part.getType();
int32_t index = part.getIndex(); if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) {
sb.append(msgString, prevIndex, index - prevIndex); break;
} elseif (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
sb.append(msgString, prevIndex, index - prevIndex); if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { const PluralSelectorContext &pluralNumber =
*static_cast<const PluralSelectorContext *>(plNumber); if(pluralNumber.forReplaceNumber) { // number-offset was already formatted.
sb.append(pluralNumber.numberString);
} else { const NumberFormat* nf = getDefaultNumberFormat(success);
sb.append(nf->format(pluralNumber.number, sb, success));
}
}
prevIndex = part.getLimit();
} elseif (type == UMSGPAT_PART_TYPE_ARG_START) {
sb.append(msgString, prevIndex, index - prevIndex);
prevIndex = index;
i = msgPattern.getLimitPartIndex(i);
index = msgPattern.getPart(i).getLimit();
MessageImpl::appendReducedApostrophes(msgString, prevIndex, index, sb);
prevIndex = index;
}
} if (sb.indexOf(LEFT_CURLY_BRACE) >= 0) {
UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter.
MessageFormat subMsgFormat(emptyPattern, fLocale, success);
subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, nullptr, success);
subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, nullptr, success);
} else {
appendTo.append(sb);
}
}
UnicodeString MessageFormat::getLiteralStringUntilNextArgument(int32_t from) const { const UnicodeString& msgString=msgPattern.getPatternString();
int32_t prevIndex=msgPattern.getPart(from).getLimit();
UnicodeString b; for (int32_t i = from + 1; ; ++i) { const MessagePattern::Part& part = msgPattern.getPart(i); const UMessagePatternPartType type=part.getType();
int32_t index=part.getIndex();
b.append(msgString, prevIndex, index - prevIndex); if(type==UMSGPAT_PART_TYPE_ARG_START || type==UMSGPAT_PART_TYPE_MSG_LIMIT) { return b;
} // Unexpected Part "part" in parsed message.
U_ASSERT(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR);
prevIndex=part.getLimit();
}
}
FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_t /*prevLength*/,
FieldPosition* /*fp*/, const Formattable* /*argId*/) const { // Unlike in Java, there are no field attributes defined for MessageFormat. Do nothing. return nullptr; /* if (fp != nullptr && Field.ARGUMENT.equals(fp.getFieldAttribute())) { fp->setBeginIndex(prevLength); fp->setEndIndex(dest.get_length()); return nullptr; } return fp;
*/
}
int32_t
MessageFormat::findOtherSubMessage(int32_t partIndex) const {
int32_t count=msgPattern.countParts(); const MessagePattern::Part *part = &msgPattern.getPart(partIndex); if(MessagePattern::Part::hasNumericValue(part->getType())) {
++partIndex;
} // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples // until ARG_LIMIT or end of plural-only pattern.
UnicodeString other(false, OTHER_STRING, 5); do {
part=&msgPattern.getPart(partIndex++);
UMessagePatternPartType type=part->getType(); if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { break;
}
U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR); // part is an ARG_SELECTOR followed by an optional explicit value, and then a message if(msgPattern.partSubstringMatches(*part, other)) { return partIndex;
} if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) {
++partIndex; // skip the numeric-value part of "=1" etc.
}
partIndex=msgPattern.getLimitPartIndex(partIndex);
} while(++partIndex<count); return 0;
}
void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) { // Deep copy pointer fields. // We need not copy the formatAliases because they are re-filled // in each getFormats() call. // The defaultNumberFormat, defaultDateFormat and pluralProvider.rules // also get created on demand.
argTypeCount = that.argTypeCount; if (argTypeCount > 0) { if (!allocateArgTypes(argTypeCount, ec)) { return;
}
uprv_memcpy(argTypes, that.argTypes, argTypeCount * sizeof(argTypes[0]));
} if (cachedFormatters != nullptr) {
uhash_removeAll(cachedFormatters);
} if (customFormatArgStarts != nullptr) {
uhash_removeAll(customFormatArgStarts);
} if (that.cachedFormatters) { if (cachedFormatters == nullptr) {
cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong,
equalFormatsForHash, &ec); if (U_FAILURE(ec)) { return;
}
uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject);
}
if(cachedFormatters!=nullptr && (formatter = getCachedFormatter(i - 2))!=nullptr) { // Just parse using the formatter.
tempStatus.setIndex(sourceOffset);
formatter->parseObject(source, argResult, tempStatus); if (tempStatus.getIndex() == sourceOffset) {
pos.setErrorIndex(sourceOffset); return nullptr; // leave index as is to signal error
}
sourceOffset = tempStatus.getIndex();
haveArgResult = true;
} elseif(
argType==UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i -2))) { // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table. // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check // for the hash table containing DummyFormat.
// Match as a string. // if at end, use longest possible match // otherwise uses first match to intervening string // does NOT recursively try all possibilities
UnicodeString stringAfterArgument = getLiteralStringUntilNextArgument(argLimit);
int32_t next; if (!stringAfterArgument.isEmpty()) {
next = source.indexOf(stringAfterArgument, sourceOffset);
} else {
next = source.length();
} if (next < 0) {
pos.setErrorIndex(sourceOffset); return nullptr; // leave index as is to signal error
} else {
UnicodeString strValue(source.tempSubString(sourceOffset, next - sourceOffset));
UnicodeString compValue;
compValue.append(LEFT_CURLY_BRACE);
itos(argNumber, compValue);
compValue.append(RIGHT_CURLY_BRACE); if (0 != strValue.compare(compValue)) {
argResult.setString(strValue);
haveArgResult = true;
}
sourceOffset = next;
}
} elseif(argType==UMSGPAT_ARG_TYPE_CHOICE) {
tempStatus.setIndex(sourceOffset); double choiceResult = ChoiceFormat::parseArgument(msgPattern, i, source, tempStatus); if (tempStatus.getIndex() == sourceOffset) {
pos.setErrorIndex(sourceOffset); return nullptr; // leave index as is to signal error
}
argResult.setDouble(choiceResult);
haveArgResult = true;
sourceOffset = tempStatus.getIndex();
} elseif(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) || argType==UMSGPAT_ARG_TYPE_SELECT) { // Parsing not supported.
ec = U_UNSUPPORTED_ERROR; return nullptr;
} else { // This should never happen.
ec = U_INTERNAL_PROGRAM_ERROR; return nullptr;
} if (haveArgResult && count <= argNumber) {
count = argNumber + 1;
}
prevIndex=msgPattern.getPart(argLimit).getLimit();
i=argLimit;
}
} // ------------------------------------- // Parses the source pattern and returns the Formattable objects array, // the array count and the ending parse position. The caller of this method // owns the array.
// ------------------------------------- // Parses the source string and returns the array of // Formattable objects and the array count. The caller // owns the returned array.
Formattable*
MessageFormat::parse(const UnicodeString& source,
int32_t& cnt,
UErrorCode& success) const
{ if (msgPattern.hasNamedArguments()) {
success = U_ARGUMENT_TYPE_MISMATCH; return nullptr;
}
ParsePosition status(0); // Calls the actual implementation method and starts // from zero offset of the source text.
Formattable* result = parse(source, status, cnt); if (status.getIndex() == 0) {
success = U_MESSAGE_PARSE_ERROR; delete[] result; return nullptr;
} return result;
}
// ------------------------------------- // Parses the source text and copy into the result buffer.
void MessageFormat::cacheExplicitFormats(UErrorCode& status) { if (U_FAILURE(status)) { return;
}
if (cachedFormatters != nullptr) {
uhash_removeAll(cachedFormatters);
} if (customFormatArgStarts != nullptr) {
uhash_removeAll(customFormatArgStarts);
}
// The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT // which we need not examine.
int32_t limit = msgPattern.countParts() - 2;
argTypeCount = 0; // We also need not look at the first two "parts" // (at most MSG_START and ARG_START) in this loop. // We determine the argTypeCount first so that we can allocateArgTypes // so that the next loop can set argTypes[argNumber]. // (This is for the C API which needs the argTypes to read its va_arg list.) for (int32_t i = 2; i < limit && U_SUCCESS(status); ++i) { const MessagePattern::Part& part = msgPattern.getPart(i); if (part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { constint argNumber = part.getValue(); if (argNumber >= argTypeCount) {
argTypeCount = argNumber + 1;
}
}
} if (!allocateArgTypes(argTypeCount, status)) { return;
} // Set all argTypes to kObject, as a "none" value, for lack of any better value. // We never use kObject for real arguments. // We use it as "no argument yet" for the check for hasArgTypeConflicts. for (int32_t i = 0; i < argTypeCount; ++i) {
argTypes[i] = Formattable::kObject;
}
hasArgTypeConflicts = false;
// This loop starts at part index 1 because we do need to examine // ARG_START parts. (But we can ignore the MSG_START.) for (int32_t i = 1; i < limit && U_SUCCESS(status); ++i) { const MessagePattern::Part* part = &msgPattern.getPart(i); if (part->getType() != UMSGPAT_PART_TYPE_ARG_START) { continue;
}
UMessagePatternArgType argType = part->getArgType();
int32_t argNumber = -1;
part = &msgPattern.getPart(i + 1); if (part->getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) {
argNumber = part->getValue();
}
Formattable::Type formattableType;
switch (argType) { case UMSGPAT_ARG_TYPE_NONE:
formattableType = Formattable::kString; break; case UMSGPAT_ARG_TYPE_SIMPLE: {
int32_t index = i;
i += 2;
UnicodeString explicitType = msgPattern.getSubstring(msgPattern.getPart(i++));
UnicodeString style; if ((part = &msgPattern.getPart(i))->getType() == UMSGPAT_PART_TYPE_ARG_STYLE) {
style = msgPattern.getSubstring(*part);
++i;
}
UParseError parseError;
Format* formatter = createAppropriateFormat(explicitType, style, formattableType, parseError, status);
setArgStartFormat(index, formatter, status); break;
} case UMSGPAT_ARG_TYPE_CHOICE: case UMSGPAT_ARG_TYPE_PLURAL: case UMSGPAT_ARG_TYPE_SELECTORDINAL:
formattableType = Formattable::kDouble; break; case UMSGPAT_ARG_TYPE_SELECT:
formattableType = Formattable::kString; break; default:
status = U_INTERNAL_PROGRAM_ERROR; // Should be unreachable.
formattableType = Formattable::kString; break;
} if (argNumber != -1) { if (argTypes[argNumber] != Formattable::kObject && argTypes[argNumber] != formattableType) {
hasArgTypeConflicts = true;
}
argTypes[argNumber] = formattableType;
}
}
}
//------------------------------------- // Finds the string, s, in the string array, list.
int32_t MessageFormat::findKeyword(const UnicodeString& s, const char16_t * const *list)
{ if (s.isEmpty()) { return 0; // default
}
int32_t length = s.length(); const char16_t *ps = PatternProps::trimWhiteSpace(s.getBuffer(), length);
UnicodeString buffer(false, ps, length); // Trims the space characters and turns all characters // in s to lower case.
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.56 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.