int32_t SelectFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, const UnicodeString& keyword, UErrorCode& ec) { if (U_FAILURE(ec)) { return 0; } UnicodeString other(FALSE, SELECT_KEYWORD_OTHER, 5); int32_t count = pattern.countParts(); int32_t msgStart=0; // Iterate over (ARG_SELECTOR, message) pairs until ARG_LIMIT or end of select-only pattern. do { const MessagePattern::Part& part=pattern.getPart(partIndex++); const UMessagePatternPartType type=part.getType(); if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { break; } // part is an ARG_SELECTOR followed by a message if(pattern.partSubstringMatches(part, keyword)) { // keyword matches return partIndex; } else if(msgStart==0 && pattern.partSubstringMatches(part, other)) { msgStart=partIndex; } partIndex=pattern.getLimitPartIndex(partIndex); } while(++partIndex<count); return msgStart; }
double ChoiceFormat::parseArgument( const MessagePattern &pattern, int32_t partIndex, const UnicodeString &source, ParsePosition &pos) { // find the best number (defined as the one with the longest parse) int32_t start = pos.getIndex(); int32_t furthest = start; double bestNumber = uprv_getNaN(); double tempNumber = 0.0; int32_t count = pattern.countParts(); while (partIndex < count && pattern.getPartType(partIndex) != UMSGPAT_PART_TYPE_ARG_LIMIT) { tempNumber = pattern.getNumericValue(pattern.getPart(partIndex)); partIndex += 2; // skip the numeric part and ignore the ARG_SELECTOR int32_t msgLimit = pattern.getLimitPartIndex(partIndex); int32_t len = matchStringUntilLimitPart(pattern, partIndex, msgLimit, source, start); if (len >= 0) { int32_t newIndex = start + len; if (newIndex > furthest) { furthest = newIndex; bestNumber = tempNumber; if (furthest == source.length()) { break; } } } partIndex = msgLimit + 1; } if (furthest == start) { pos.setErrorIndex(start); } else { pos.setIndex(furthest); } return bestNumber; }
int32_t ChoiceFormat::matchStringUntilLimitPart( const MessagePattern &pattern, int32_t partIndex, int32_t limitPartIndex, const UnicodeString &source, int32_t sourceOffset) { int32_t matchingSourceLength = 0; const UnicodeString &msgString = pattern.getPatternString(); int32_t prevIndex = pattern.getPart(partIndex).getLimit(); for (;;) { const MessagePattern::Part &part = pattern.getPart(++partIndex); if (partIndex == limitPartIndex || part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { int32_t index = part.getIndex(); int32_t length = index - prevIndex; if (length != 0 && 0 != source.compare(sourceOffset, length, msgString, prevIndex, length)) { return -1; // mismatch } matchingSourceLength += length; if (partIndex == limitPartIndex) { return matchingSourceLength; } prevIndex = part.getLimit(); // SKIP_SYNTAX } } }
int32_t ChoiceFormat::findSubMessage(const MessagePattern &pattern, int32_t partIndex, double number) { int32_t count = pattern.countParts(); int32_t msgStart; // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples // until ARG_LIMIT or end of choice-only pattern. // Ignore the first number and selector and start the loop on the first message. partIndex += 2; for (;;) { // Skip but remember the current sub-message. msgStart = partIndex; partIndex = pattern.getLimitPartIndex(partIndex); if (++partIndex >= count) { // Reached the end of the choice-only pattern. // Return with the last sub-message. break; } const MessagePattern::Part &part = pattern.getPart(partIndex++); UMessagePatternPartType type = part.getType(); if (type == UMSGPAT_PART_TYPE_ARG_LIMIT) { // Reached the end of the ChoiceFormat style. // Return with the last sub-message. break; } // part is an ARG_INT or ARG_DOUBLE U_ASSERT(MessagePattern::Part::hasNumericValue(type)); double boundary = pattern.getNumericValue(part); // Fetch the ARG_SELECTOR character. int32_t selectorIndex = pattern.getPatternIndex(partIndex++); UChar boundaryChar = pattern.getPatternString().charAt(selectorIndex); if (boundaryChar == LESS_THAN ? !(number > boundary) : !(number >= boundary)) { // The number is in the interval between the previous boundary and the current one. // Return with the sub-message between them. // The !(a>b) and !(a>=b) comparisons are equivalent to // (a<=b) and (a<b) except they "catch" NaN. break; } } return msgStart; }
int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, const PluralSelector& selector, void *context, double number, UErrorCode& ec) { if (U_FAILURE(ec)) { return 0; } int32_t count=pattern.countParts(); double offset; const MessagePattern::Part* part=&pattern.getPart(partIndex); if (MessagePattern::Part::hasNumericValue(part->getType())) { offset=pattern.getNumericValue(*part); ++partIndex; } else { offset=0; } // The keyword is empty until we need to match against a non-explicit, not-"other" value. // Then we get the keyword from the selector. // (In other words, we never call the selector if we match against an explicit value, // or if the only non-explicit keyword is "other".) UnicodeString keyword; UnicodeString other(FALSE, OTHER_STRING, 5); // When we find a match, we set msgStart>0 and also set this boolean to true // to avoid matching the keyword again (duplicates are allowed) // while we continue to look for an explicit-value match. UBool haveKeywordMatch=FALSE; // msgStart is 0 until we find any appropriate sub-message. // We remember the first "other" sub-message if we have not seen any // appropriate sub-message before. // We remember the first matching-keyword sub-message if we have not seen // one of those before. // (The parser allows [does not check for] duplicate keywords. // We just have to make sure to take the first one.) // We avoid matching the keyword twice by also setting haveKeywordMatch=true // at the first keyword match. // We keep going until we find an explicit-value match or reach the end of the plural style. int32_t msgStart=0; // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples // until ARG_LIMIT or end of plural-only pattern. do { part=&pattern.getPart(partIndex++); const 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(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) { // explicit value like "=2" part=&pattern.getPart(partIndex++); if(number==pattern.getNumericValue(*part)) { // matches explicit value return partIndex; } } else if(!haveKeywordMatch) { // plural keyword like "few" or "other" // Compare "other" first and call the selector if this is not "other". if(pattern.partSubstringMatches(*part, other)) { if(msgStart==0) { msgStart=partIndex; if(0 == keyword.compare(other)) { // This is the first "other" sub-message, // and the selected keyword is also "other". // Do not match "other" again. haveKeywordMatch=TRUE; } } } else { if(keyword.isEmpty()) { keyword=selector.select(context, number-offset, ec); if(msgStart!=0 && (0 == keyword.compare(other))) { // We have already seen an "other" sub-message. // Do not match "other" again. haveKeywordMatch=TRUE; // Skip keyword matching but do getLimitPartIndex(). } } if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) { // keyword matches msgStart=partIndex; // Do not match this keyword again. haveKeywordMatch=TRUE; } } } partIndex=pattern.getLimitPartIndex(partIndex); } while(++partIndex<count); return msgStart; }