+ appendTo.append(arg->getString(success));
+ }
+ } else if (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.
+ const double number = arg->getDouble(success);
+ int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number);
+ formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames,
+ cnt, appendTo, success);
+ } else if (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);
+ } else if (argType == UMSGPAT_ARG_TYPE_SELECT) {
+ int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success);
+ formatComplexSubMessage(subMsgStart, NULL, 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;
+ }
+}
+
+
+void MessageFormat::formatComplexSubMessage(int32_t msgStart,
+ const void *plNumber,
+ const Formattable* arguments,
+ const UnicodeString *argumentNames,
+ int32_t cnt,
+ AppendableWrapper& appendTo,
+ UErrorCode& success) const {
+ if (U_FAILURE(success)) {
+ return;
+ }
+
+ if (!MessageImpl::jdkAposMode(msgPattern)) {
+ format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, NULL, success);
+ return;
+ }
+
+ // 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;
+ } else if (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();
+ } else if (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, NULL, success);
+ subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, NULL, 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 NULL;
+ /*
+ if (fp != NULL && Field.ARGUMENT.equals(fp.getFieldAttribute())) {
+ fp->setBeginIndex(prevLength);
+ fp->setEndIndex(dest.get_length());
+ return NULL;
+ }
+ 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;
+}
+
+int32_t
+MessageFormat::findFirstPluralNumberArg(int32_t msgStart, const UnicodeString &argName) const {
+ for(int32_t i=msgStart+1;; ++i) {
+ const MessagePattern::Part &part=msgPattern.getPart(i);
+ UMessagePatternPartType type=part.getType();
+ if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
+ return 0;
+ }
+ if(type==UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
+ return -1;
+ }
+ if(type==UMSGPAT_PART_TYPE_ARG_START) {
+ UMessagePatternArgType argType=part.getArgType();
+ if(!argName.isEmpty() && (argType==UMSGPAT_ARG_TYPE_NONE || argType==UMSGPAT_ARG_TYPE_SIMPLE)) {
+ // ARG_NUMBER or ARG_NAME
+ if(msgPattern.partSubstringMatches(msgPattern.getPart(i+1), argName)) {
+ return i;
+ }