1 /*
2 *******************************************************************************
3 * Copyright (C) 2011, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
6 * file name: messagepattern.cpp
7 * encoding: US-ASCII
8 * tab size: 8 (not used)
9 * indentation:4
10 *
11 * created on: 2011mar14
12 * created by: Markus W. Scherer
13 */
14
15 #include "unicode/utypes.h"
16
17 #if !UCONFIG_NO_FORMATTING
18
19 #include "unicode/messagepattern.h"
20 #include "unicode/unistr.h"
21 #include "cmemory.h"
22 #include "cstring.h"
23 #include "messageimpl.h"
24 #include "patternprops.h"
25 #include "putilimp.h"
26 #include "uassert.h"
27
28 U_NAMESPACE_BEGIN
29
30 // Unicode character/code point constants ---------------------------------- ***
31
32 static const UChar u_pound=0x23;
33 static const UChar u_apos=0x27;
34 static const UChar u_plus=0x2B;
35 static const UChar u_comma=0x2C;
36 static const UChar u_minus=0x2D;
37 static const UChar u_dot=0x2E;
38 static const UChar u_colon=0x3A;
39 static const UChar u_lessThan=0x3C;
40 static const UChar u_equal=0x3D;
41 static const UChar u_A=0x41;
42 static const UChar u_C=0x43;
43 static const UChar u_E=0x45;
44 static const UChar u_H=0x48;
45 static const UChar u_I=0x49;
46 static const UChar u_L=0x4C;
47 static const UChar u_O=0x4F;
48 static const UChar u_P=0x50;
49 static const UChar u_R=0x52;
50 static const UChar u_S=0x53;
51 static const UChar u_T=0x54;
52 static const UChar u_U=0x55;
53 static const UChar u_Z=0x5A;
54 static const UChar u_a=0x61;
55 static const UChar u_c=0x63;
56 static const UChar u_e=0x65;
57 static const UChar u_f=0x66;
58 static const UChar u_h=0x68;
59 static const UChar u_i=0x69;
60 static const UChar u_l=0x6C;
61 static const UChar u_o=0x6F;
62 static const UChar u_p=0x70;
63 static const UChar u_r=0x72;
64 static const UChar u_s=0x73;
65 static const UChar u_t=0x74;
66 static const UChar u_u=0x75;
67 static const UChar u_z=0x7A;
68 static const UChar u_leftCurlyBrace=0x7B;
69 static const UChar u_pipe=0x7C;
70 static const UChar u_rightCurlyBrace=0x7D;
71 static const UChar u_lessOrEqual=0x2264; // U+2264 is <=
72
73 static const UChar kOffsetColon[]={ // "offset:"
74 u_o, u_f, u_f, u_s, u_e, u_t, u_colon
75 };
76
77 static const UChar kOther[]={ // "other"
78 u_o, u_t, u_h, u_e, u_r
79 };
80
81 // MessagePatternList ------------------------------------------------------ ***
82
83 template<typename T, int32_t stackCapacity>
84 class MessagePatternList : public UMemory {
85 public:
MessagePatternList()86 MessagePatternList() {}
87 void copyFrom(const MessagePatternList<T, stackCapacity> &other,
88 int32_t length,
89 UErrorCode &errorCode);
90 UBool ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode);
memEquals(const MessagePatternList<T,stackCapacity> & other,int32_t length) const91 UBool memEquals(const MessagePatternList<T, stackCapacity> &other, int32_t length) const {
92 return 0==uprv_memcmp(a.getAlias(), other.a.getAlias(), length*sizeof(T));
93 }
94
95 MaybeStackArray<T, stackCapacity> a;
96 };
97
98 template<typename T, int32_t stackCapacity>
99 void
copyFrom(const MessagePatternList<T,stackCapacity> & other,int32_t length,UErrorCode & errorCode)100 MessagePatternList<T, stackCapacity>::copyFrom(
101 const MessagePatternList<T, stackCapacity> &other,
102 int32_t length,
103 UErrorCode &errorCode) {
104 if(U_SUCCESS(errorCode) && length>0) {
105 if(length>a.getCapacity() && NULL==a.resize(length)) {
106 errorCode=U_MEMORY_ALLOCATION_ERROR;
107 return;
108 }
109 uprv_memcpy(a.getAlias(), other.a.getAlias(), length*sizeof(T));
110 }
111 }
112
113 template<typename T, int32_t stackCapacity>
114 UBool
ensureCapacityForOneMore(int32_t oldLength,UErrorCode & errorCode)115 MessagePatternList<T, stackCapacity>::ensureCapacityForOneMore(int32_t oldLength, UErrorCode &errorCode) {
116 if(U_FAILURE(errorCode)) {
117 return FALSE;
118 }
119 if(a.getCapacity()>oldLength || a.resize(2*oldLength, oldLength)!=NULL) {
120 return TRUE;
121 }
122 errorCode=U_MEMORY_ALLOCATION_ERROR;
123 return FALSE;
124 }
125
126 // MessagePatternList specializations -------------------------------------- ***
127
128 class MessagePatternDoubleList : public MessagePatternList<double, 8> {
129 };
130
131 class MessagePatternPartsList : public MessagePatternList<MessagePattern::Part, 32> {
132 };
133
134 // MessagePattern constructors etc. ---------------------------------------- ***
135
MessagePattern(UErrorCode & errorCode)136 MessagePattern::MessagePattern(UErrorCode &errorCode)
137 : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE),
138 partsList(NULL), parts(NULL), partsLength(0),
139 numericValuesList(NULL), numericValues(NULL), numericValuesLength(0),
140 hasArgNames(FALSE), hasArgNumbers(FALSE), needsAutoQuoting(FALSE) {
141 init(errorCode);
142 }
143
MessagePattern(UMessagePatternApostropheMode mode,UErrorCode & errorCode)144 MessagePattern::MessagePattern(UMessagePatternApostropheMode mode, UErrorCode &errorCode)
145 : aposMode(mode),
146 partsList(NULL), parts(NULL), partsLength(0),
147 numericValuesList(NULL), numericValues(NULL), numericValuesLength(0),
148 hasArgNames(FALSE), hasArgNumbers(FALSE), needsAutoQuoting(FALSE) {
149 init(errorCode);
150 }
151
MessagePattern(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)152 MessagePattern::MessagePattern(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode)
153 : aposMode(UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE),
154 partsList(NULL), parts(NULL), partsLength(0),
155 numericValuesList(NULL), numericValues(NULL), numericValuesLength(0),
156 hasArgNames(FALSE), hasArgNumbers(FALSE), needsAutoQuoting(FALSE) {
157 if(init(errorCode)) {
158 parse(pattern, parseError, errorCode);
159 }
160 }
161
162 UBool
init(UErrorCode & errorCode)163 MessagePattern::init(UErrorCode &errorCode) {
164 if(U_FAILURE(errorCode)) {
165 return FALSE;
166 }
167 partsList=new MessagePatternPartsList();
168 if(partsList==NULL) {
169 errorCode=U_MEMORY_ALLOCATION_ERROR;
170 return FALSE;
171 }
172 parts=partsList->a.getAlias();
173 return TRUE;
174 }
175
MessagePattern(const MessagePattern & other)176 MessagePattern::MessagePattern(const MessagePattern &other)
177 : aposMode(other.aposMode), msg(other.msg),
178 partsList(NULL), parts(NULL), partsLength(0),
179 numericValuesList(NULL), numericValues(NULL), numericValuesLength(0),
180 hasArgNames(other.hasArgNames), hasArgNumbers(other.hasArgNumbers),
181 needsAutoQuoting(other.needsAutoQuoting) {
182 UErrorCode errorCode=U_ZERO_ERROR;
183 if(!copyStorage(other, errorCode)) {
184 clear();
185 }
186 }
187
188 MessagePattern &
operator =(const MessagePattern & other)189 MessagePattern::operator=(const MessagePattern &other) {
190 if(this==&other) {
191 return *this;
192 }
193 aposMode=other.aposMode;
194 msg=other.msg;
195 hasArgNames=other.hasArgNames;
196 hasArgNumbers=other.hasArgNumbers;
197 needsAutoQuoting=other.needsAutoQuoting;
198 UErrorCode errorCode=U_ZERO_ERROR;
199 if(!copyStorage(other, errorCode)) {
200 clear();
201 }
202 return *this;
203 }
204
205 UBool
copyStorage(const MessagePattern & other,UErrorCode & errorCode)206 MessagePattern::copyStorage(const MessagePattern &other, UErrorCode &errorCode) {
207 if(U_FAILURE(errorCode)) {
208 return FALSE;
209 }
210 parts=NULL;
211 partsLength=0;
212 numericValues=NULL;
213 numericValuesLength=0;
214 if(partsList==NULL) {
215 partsList=new MessagePatternPartsList();
216 if(partsList==NULL) {
217 errorCode=U_MEMORY_ALLOCATION_ERROR;
218 return FALSE;
219 }
220 parts=partsList->a.getAlias();
221 }
222 if(other.partsLength>0) {
223 partsList->copyFrom(*other.partsList, other.partsLength, errorCode);
224 if(U_FAILURE(errorCode)) {
225 return FALSE;
226 }
227 parts=partsList->a.getAlias();
228 partsLength=other.partsLength;
229 }
230 if(other.numericValuesLength>0) {
231 if(numericValuesList==NULL) {
232 numericValuesList=new MessagePatternDoubleList();
233 if(numericValuesList==NULL) {
234 errorCode=U_MEMORY_ALLOCATION_ERROR;
235 return FALSE;
236 }
237 numericValues=numericValuesList->a.getAlias();
238 }
239 numericValuesList->copyFrom(
240 *other.numericValuesList, other.numericValuesLength, errorCode);
241 if(U_FAILURE(errorCode)) {
242 return FALSE;
243 }
244 numericValues=numericValuesList->a.getAlias();
245 numericValuesLength=other.numericValuesLength;
246 }
247 return TRUE;
248 }
249
~MessagePattern()250 MessagePattern::~MessagePattern() {
251 delete partsList;
252 delete numericValuesList;
253 }
254
255 // MessagePattern API ------------------------------------------------------ ***
256
257 MessagePattern &
parse(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)258 MessagePattern::parse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) {
259 preParse(pattern, parseError, errorCode);
260 parseMessage(0, 0, 0, UMSGPAT_ARG_TYPE_NONE, parseError, errorCode);
261 postParse();
262 return *this;
263 }
264
265 MessagePattern &
parseChoiceStyle(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)266 MessagePattern::parseChoiceStyle(const UnicodeString &pattern,
267 UParseError *parseError, UErrorCode &errorCode) {
268 preParse(pattern, parseError, errorCode);
269 parseChoiceStyle(0, 0, parseError, errorCode);
270 postParse();
271 return *this;
272 }
273
274 MessagePattern &
parsePluralStyle(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)275 MessagePattern::parsePluralStyle(const UnicodeString &pattern,
276 UParseError *parseError, UErrorCode &errorCode) {
277 preParse(pattern, parseError, errorCode);
278 parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_PLURAL, 0, 0, parseError, errorCode);
279 postParse();
280 return *this;
281 }
282
283 MessagePattern &
parseSelectStyle(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)284 MessagePattern::parseSelectStyle(const UnicodeString &pattern,
285 UParseError *parseError, UErrorCode &errorCode) {
286 preParse(pattern, parseError, errorCode);
287 parsePluralOrSelectStyle(UMSGPAT_ARG_TYPE_SELECT, 0, 0, parseError, errorCode);
288 postParse();
289 return *this;
290 }
291
292 void
clear()293 MessagePattern::clear() {
294 // Mostly the same as preParse().
295 msg.remove();
296 hasArgNames=hasArgNumbers=FALSE;
297 needsAutoQuoting=FALSE;
298 partsLength=0;
299 numericValuesLength=0;
300 }
301
302 UBool
operator ==(const MessagePattern & other) const303 MessagePattern::operator==(const MessagePattern &other) const {
304 if(this==&other) {
305 return TRUE;
306 }
307 return
308 aposMode==other.aposMode &&
309 msg==other.msg &&
310 // parts.equals(o.parts)
311 partsLength==other.partsLength &&
312 (partsLength==0 || partsList->memEquals(*other.partsList, partsLength));
313 // No need to compare numericValues if msg and parts are the same.
314 }
315
316 int32_t
hashCode() const317 MessagePattern::hashCode() const {
318 int32_t hash=(aposMode*37+msg.hashCode())*37+partsLength;
319 for(int32_t i=0; i<partsLength; ++i) {
320 hash=hash*37+parts[i].hashCode();
321 }
322 return hash;
323 }
324
325 int32_t
validateArgumentName(const UnicodeString & name)326 MessagePattern::validateArgumentName(const UnicodeString &name) {
327 if(!PatternProps::isIdentifier(name.getBuffer(), name.length())) {
328 return UMSGPAT_ARG_NAME_NOT_VALID;
329 }
330 return parseArgNumber(name, 0, name.length());
331 }
332
333 UnicodeString
autoQuoteApostropheDeep() const334 MessagePattern::autoQuoteApostropheDeep() const {
335 if(!needsAutoQuoting) {
336 return msg;
337 }
338 UnicodeString modified(msg);
339 // Iterate backward so that the insertion indexes do not change.
340 int32_t count=countParts();
341 for(int32_t i=count; i>0;) {
342 const Part &part=getPart(--i);
343 if(part.getType()==UMSGPAT_PART_TYPE_INSERT_CHAR) {
344 modified.insert(part.index, (UChar)part.value);
345 }
346 }
347 return modified;
348 }
349
350 double
getNumericValue(const Part & part) const351 MessagePattern::getNumericValue(const Part &part) const {
352 UMessagePatternPartType type=part.type;
353 if(type==UMSGPAT_PART_TYPE_ARG_INT) {
354 return part.value;
355 } else if(type==UMSGPAT_PART_TYPE_ARG_DOUBLE) {
356 return numericValues[part.value];
357 } else {
358 return UMSGPAT_NO_NUMERIC_VALUE;
359 }
360 }
361
362 /**
363 * Returns the "offset:" value of a PluralFormat argument, or 0 if none is specified.
364 * @param pluralStart the index of the first PluralFormat argument style part. (0..countParts()-1)
365 * @return the "offset:" value.
366 * @draft ICU 4.8
367 */
368 double
getPluralOffset(int32_t pluralStart) const369 MessagePattern::getPluralOffset(int32_t pluralStart) const {
370 const Part &part=getPart(pluralStart);
371 if(Part::hasNumericValue(part.type)) {
372 return getNumericValue(part);
373 } else {
374 return 0;
375 }
376 }
377
378 // MessagePattern::Part ---------------------------------------------------- ***
379
380 UBool
operator ==(const Part & other) const381 MessagePattern::Part::operator==(const Part &other) const {
382 if(this==&other) {
383 return TRUE;
384 }
385 return
386 type==other.type &&
387 index==other.index &&
388 length==other.length &&
389 value==other.value &&
390 limitPartIndex==other.limitPartIndex;
391 }
392
393 // MessagePattern parser --------------------------------------------------- ***
394
395 void
preParse(const UnicodeString & pattern,UParseError * parseError,UErrorCode & errorCode)396 MessagePattern::preParse(const UnicodeString &pattern, UParseError *parseError, UErrorCode &errorCode) {
397 if(U_FAILURE(errorCode)) {
398 return;
399 }
400 if(parseError!=NULL) {
401 parseError->line=0;
402 parseError->offset=0;
403 parseError->preContext[0]=0;
404 parseError->postContext[0]=0;
405 }
406 msg=pattern;
407 hasArgNames=hasArgNumbers=FALSE;
408 needsAutoQuoting=FALSE;
409 partsLength=0;
410 numericValuesLength=0;
411 }
412
413 void
postParse()414 MessagePattern::postParse() {
415 if(partsList!=NULL) {
416 parts=partsList->a.getAlias();
417 }
418 if(numericValuesList!=NULL) {
419 numericValues=numericValuesList->a.getAlias();
420 }
421 }
422
423 int32_t
parseMessage(int32_t index,int32_t msgStartLength,int32_t nestingLevel,UMessagePatternArgType parentType,UParseError * parseError,UErrorCode & errorCode)424 MessagePattern::parseMessage(int32_t index, int32_t msgStartLength,
425 int32_t nestingLevel, UMessagePatternArgType parentType,
426 UParseError *parseError, UErrorCode &errorCode) {
427 if(U_FAILURE(errorCode)) {
428 return 0;
429 }
430 if(nestingLevel>Part::MAX_VALUE) {
431 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
432 return 0;
433 }
434 int32_t msgStart=partsLength;
435 addPart(UMSGPAT_PART_TYPE_MSG_START, index, msgStartLength, nestingLevel, errorCode);
436 index+=msgStartLength;
437 for(;;) { // while(index<msg.length()) with U_FAILURE(errorCode) check
438 if(U_FAILURE(errorCode)) {
439 return 0;
440 }
441 if(index>=msg.length()) {
442 break;
443 }
444 UChar c=msg.charAt(index++);
445 if(c==u_apos) {
446 if(index==msg.length()) {
447 // The apostrophe is the last character in the pattern.
448 // Add a Part for auto-quoting.
449 addPart(UMSGPAT_PART_TYPE_INSERT_CHAR, index, 0,
450 u_apos, errorCode); // value=char to be inserted
451 needsAutoQuoting=TRUE;
452 } else {
453 c=msg.charAt(index);
454 if(c==u_apos) {
455 // double apostrophe, skip the second one
456 addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index++, 1, 0, errorCode);
457 } else if(
458 aposMode==UMSGPAT_APOS_DOUBLE_REQUIRED ||
459 c==u_leftCurlyBrace || c==u_rightCurlyBrace ||
460 (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe) ||
461 (parentType==UMSGPAT_ARG_TYPE_PLURAL && c==u_pound)
462 ) {
463 // skip the quote-starting apostrophe
464 addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index-1, 1, 0, errorCode);
465 // find the end of the quoted literal text
466 for(;;) {
467 index=msg.indexOf(u_apos, index+1);
468 if(index>=0) {
469 if(/*(index+1)<msg.length() &&*/ msg.charAt(index+1)==u_apos) {
470 // double apostrophe inside quoted literal text
471 // still encodes a single apostrophe, skip the second one
472 addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, ++index, 1, 0, errorCode);
473 } else {
474 // skip the quote-ending apostrophe
475 addPart(UMSGPAT_PART_TYPE_SKIP_SYNTAX, index++, 1, 0, errorCode);
476 break;
477 }
478 } else {
479 // The quoted text reaches to the end of the of the message.
480 index=msg.length();
481 // Add a Part for auto-quoting.
482 addPart(UMSGPAT_PART_TYPE_INSERT_CHAR, index, 0,
483 u_apos, errorCode); // value=char to be inserted
484 needsAutoQuoting=TRUE;
485 break;
486 }
487 }
488 } else {
489 // Interpret the apostrophe as literal text.
490 // Add a Part for auto-quoting.
491 addPart(UMSGPAT_PART_TYPE_INSERT_CHAR, index, 0,
492 u_apos, errorCode); // value=char to be inserted
493 needsAutoQuoting=TRUE;
494 }
495 }
496 } else if(parentType==UMSGPAT_ARG_TYPE_PLURAL && c==u_pound) {
497 // The unquoted # in a plural message fragment will be replaced
498 // with the (number-offset).
499 addPart(UMSGPAT_PART_TYPE_REPLACE_NUMBER, index-1, 1, 0, errorCode);
500 } else if(c==u_leftCurlyBrace) {
501 index=parseArg(index-1, 1, nestingLevel, parseError, errorCode);
502 } else if((nestingLevel>0 && c==u_rightCurlyBrace) ||
503 (parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_pipe)) {
504 // Finish the message before the terminator.
505 // In a choice style, report the "}" substring only for the following ARG_LIMIT,
506 // not for this MSG_LIMIT.
507 int32_t limitLength=(parentType==UMSGPAT_ARG_TYPE_CHOICE && c==u_rightCurlyBrace) ? 0 : 1;
508 addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index-1, limitLength,
509 nestingLevel, errorCode);
510 if(parentType==UMSGPAT_ARG_TYPE_CHOICE) {
511 // Let the choice style parser see the '}' or '|'.
512 return index-1;
513 } else {
514 // continue parsing after the '}'
515 return index;
516 }
517 } // else: c is part of literal text
518 }
519 if(nestingLevel>0 && !inTopLevelChoiceMessage(nestingLevel, parentType)) {
520 setParseError(parseError, 0); // Unmatched '{' braces in message.
521 errorCode=U_UNMATCHED_BRACES;
522 return 0;
523 }
524 addLimitPart(msgStart, UMSGPAT_PART_TYPE_MSG_LIMIT, index, 0, nestingLevel, errorCode);
525 return index;
526 }
527
528 int32_t
parseArg(int32_t index,int32_t argStartLength,int32_t nestingLevel,UParseError * parseError,UErrorCode & errorCode)529 MessagePattern::parseArg(int32_t index, int32_t argStartLength, int32_t nestingLevel,
530 UParseError *parseError, UErrorCode &errorCode) {
531 int32_t argStart=partsLength;
532 UMessagePatternArgType argType=UMSGPAT_ARG_TYPE_NONE;
533 addPart(UMSGPAT_PART_TYPE_ARG_START, index, argStartLength, argType, errorCode);
534 if(U_FAILURE(errorCode)) {
535 return 0;
536 }
537 int32_t nameIndex=index=skipWhiteSpace(index+argStartLength);
538 if(index==msg.length()) {
539 setParseError(parseError, 0); // Unmatched '{' braces in message.
540 errorCode=U_UNMATCHED_BRACES;
541 return 0;
542 }
543 // parse argument name or number
544 index=skipIdentifier(index);
545 int32_t number=parseArgNumber(nameIndex, index);
546 if(number>=0) {
547 int32_t length=index-nameIndex;
548 if(length>Part::MAX_LENGTH || number>Part::MAX_VALUE) {
549 setParseError(parseError, nameIndex); // Argument number too large.
550 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
551 return 0;
552 }
553 hasArgNumbers=TRUE;
554 addPart(UMSGPAT_PART_TYPE_ARG_NUMBER, nameIndex, length, number, errorCode);
555 } else if(number==UMSGPAT_ARG_NAME_NOT_NUMBER) {
556 int32_t length=index-nameIndex;
557 if(length>Part::MAX_LENGTH) {
558 setParseError(parseError, nameIndex); // Argument name too long.
559 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
560 return 0;
561 }
562 hasArgNames=TRUE;
563 addPart(UMSGPAT_PART_TYPE_ARG_NAME, nameIndex, length, 0, errorCode);
564 } else { // number<-1 (ARG_NAME_NOT_VALID)
565 setParseError(parseError, nameIndex); // Bad argument syntax.
566 errorCode=U_PATTERN_SYNTAX_ERROR;
567 return 0;
568 }
569 index=skipWhiteSpace(index);
570 if(index==msg.length()) {
571 setParseError(parseError, 0); // Unmatched '{' braces in message.
572 errorCode=U_UNMATCHED_BRACES;
573 return 0;
574 }
575 UChar c=msg.charAt(index);
576 if(c==u_rightCurlyBrace) {
577 // all done
578 } else if(c!=u_comma) {
579 setParseError(parseError, nameIndex); // Bad argument syntax.
580 errorCode=U_PATTERN_SYNTAX_ERROR;
581 return 0;
582 } else /* ',' */ {
583 // parse argument type: case-sensitive a-zA-Z
584 int32_t typeIndex=index=skipWhiteSpace(index+1);
585 while(index<msg.length() && isArgTypeChar(msg.charAt(index))) {
586 ++index;
587 }
588 int32_t length=index-typeIndex;
589 index=skipWhiteSpace(index);
590 if(index==msg.length()) {
591 setParseError(parseError, 0); // Unmatched '{' braces in message.
592 errorCode=U_UNMATCHED_BRACES;
593 return 0;
594 }
595 if(length==0 || ((c=msg.charAt(index))!=u_comma && c!=u_rightCurlyBrace)) {
596 setParseError(parseError, nameIndex); // Bad argument syntax.
597 errorCode=U_PATTERN_SYNTAX_ERROR;
598 return 0;
599 }
600 if(length>Part::MAX_LENGTH) {
601 setParseError(parseError, nameIndex); // Argument type name too long.
602 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
603 return 0;
604 }
605 argType=UMSGPAT_ARG_TYPE_SIMPLE;
606 if(length==6) {
607 // case-insensitive comparisons for complex-type names
608 if(isChoice(typeIndex)) {
609 argType=UMSGPAT_ARG_TYPE_CHOICE;
610 } else if(isPlural(typeIndex)) {
611 argType=UMSGPAT_ARG_TYPE_PLURAL;
612 } else if(isSelect(typeIndex)) {
613 argType=UMSGPAT_ARG_TYPE_SELECT;
614 }
615 }
616 // change the ARG_START type from NONE to argType
617 partsList->a[argStart].value=(int16_t)argType;
618 if(argType==UMSGPAT_ARG_TYPE_SIMPLE) {
619 addPart(UMSGPAT_PART_TYPE_ARG_TYPE, typeIndex, length, 0, errorCode);
620 }
621 // look for an argument style (pattern)
622 if(c==u_rightCurlyBrace) {
623 if(argType!=UMSGPAT_ARG_TYPE_SIMPLE) {
624 setParseError(parseError, nameIndex); // No style field for complex argument.
625 errorCode=U_PATTERN_SYNTAX_ERROR;
626 return 0;
627 }
628 } else /* ',' */ {
629 ++index;
630 if(argType==UMSGPAT_ARG_TYPE_SIMPLE) {
631 index=parseSimpleStyle(index, parseError, errorCode);
632 } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) {
633 index=parseChoiceStyle(index, nestingLevel, parseError, errorCode);
634 } else {
635 index=parsePluralOrSelectStyle(argType, index, nestingLevel, parseError, errorCode);
636 }
637 }
638 }
639 // Argument parsing stopped on the '}'.
640 addLimitPart(argStart, UMSGPAT_PART_TYPE_ARG_LIMIT, index, 1, argType, errorCode);
641 return index+1;
642 }
643
644 int32_t
parseSimpleStyle(int32_t index,UParseError * parseError,UErrorCode & errorCode)645 MessagePattern::parseSimpleStyle(int32_t index, UParseError *parseError, UErrorCode &errorCode) {
646 if(U_FAILURE(errorCode)) {
647 return 0;
648 }
649 int32_t start=index;
650 int32_t nestedBraces=0;
651 while(index<msg.length()) {
652 UChar c=msg.charAt(index++);
653 if(c==u_apos) {
654 // Treat apostrophe as quoting but include it in the style part.
655 // Find the end of the quoted literal text.
656 index=msg.indexOf(u_apos, index);
657 if(index<0) {
658 // Quoted literal argument style text reaches to the end of the message.
659 setParseError(parseError, start);
660 errorCode=U_PATTERN_SYNTAX_ERROR;
661 return 0;
662 }
663 // skip the quote-ending apostrophe
664 ++index;
665 } else if(c==u_leftCurlyBrace) {
666 ++nestedBraces;
667 } else if(c==u_rightCurlyBrace) {
668 if(nestedBraces>0) {
669 --nestedBraces;
670 } else {
671 int32_t length=--index-start;
672 if(length>Part::MAX_LENGTH) {
673 setParseError(parseError, start); // Argument style text too long.
674 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
675 return 0;
676 }
677 addPart(UMSGPAT_PART_TYPE_ARG_STYLE, start, length, 0, errorCode);
678 return index;
679 }
680 } // c is part of literal text
681 }
682 setParseError(parseError, 0); // Unmatched '{' braces in message.
683 errorCode=U_UNMATCHED_BRACES;
684 return 0;
685 }
686
687 int32_t
parseChoiceStyle(int32_t index,int32_t nestingLevel,UParseError * parseError,UErrorCode & errorCode)688 MessagePattern::parseChoiceStyle(int32_t index, int32_t nestingLevel,
689 UParseError *parseError, UErrorCode &errorCode) {
690 if(U_FAILURE(errorCode)) {
691 return 0;
692 }
693 int32_t start=index;
694 index=skipWhiteSpace(index);
695 if(index==msg.length() || msg.charAt(index)==u_rightCurlyBrace) {
696 setParseError(parseError, 0); // Missing choice argument pattern.
697 errorCode=U_PATTERN_SYNTAX_ERROR;
698 return 0;
699 }
700 for(;;) {
701 // The choice argument style contains |-separated (number, separator, message) triples.
702 // Parse the number.
703 int32_t numberIndex=index;
704 index=skipDouble(index);
705 int32_t length=index-numberIndex;
706 if(length==0) {
707 setParseError(parseError, start); // Bad choice pattern syntax.
708 errorCode=U_PATTERN_SYNTAX_ERROR;
709 return 0;
710 }
711 if(length>Part::MAX_LENGTH) {
712 setParseError(parseError, numberIndex); // Choice number too long.
713 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
714 return 0;
715 }
716 parseDouble(numberIndex, index, TRUE, parseError, errorCode); // adds ARG_INT or ARG_DOUBLE
717 if(U_FAILURE(errorCode)) {
718 return 0;
719 }
720 // Parse the separator.
721 index=skipWhiteSpace(index);
722 if(index==msg.length()) {
723 setParseError(parseError, start); // Bad choice pattern syntax.
724 errorCode=U_PATTERN_SYNTAX_ERROR;
725 return 0;
726 }
727 UChar c=msg.charAt(index);
728 if(!(c==u_pound || c==u_lessThan || c==u_lessOrEqual)) { // U+2264 is <=
729 setParseError(parseError, start); // Expected choice separator (#<\u2264) instead of c.
730 errorCode=U_PATTERN_SYNTAX_ERROR;
731 return 0;
732 }
733 addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, index, 1, 0, errorCode);
734 // Parse the message fragment.
735 index=parseMessage(++index, 0, nestingLevel+1, UMSGPAT_ARG_TYPE_CHOICE, parseError, errorCode);
736 if(U_FAILURE(errorCode)) {
737 return 0;
738 }
739 // parseMessage(..., CHOICE) returns the index of the terminator, or msg.length().
740 if(index==msg.length()) {
741 return index;
742 }
743 if(msg.charAt(index)==u_rightCurlyBrace) {
744 if(!inMessageFormatPattern(nestingLevel)) {
745 setParseError(parseError, start); // Bad choice pattern syntax.
746 errorCode=U_PATTERN_SYNTAX_ERROR;
747 return 0;
748 }
749 return index;
750 } // else the terminator is '|'
751 index=skipWhiteSpace(index+1);
752 }
753 }
754
755 int32_t
parsePluralOrSelectStyle(UMessagePatternArgType argType,int32_t index,int32_t nestingLevel,UParseError * parseError,UErrorCode & errorCode)756 MessagePattern::parsePluralOrSelectStyle(UMessagePatternArgType argType,
757 int32_t index, int32_t nestingLevel,
758 UParseError *parseError, UErrorCode &errorCode) {
759 if(U_FAILURE(errorCode)) {
760 return 0;
761 }
762 int32_t start=index;
763 UBool isEmpty=TRUE;
764 UBool hasOther=FALSE;
765 for(;;) {
766 // First, collect the selector looking for a small set of terminators.
767 // It would be a little faster to consider the syntax of each possible
768 // token right here, but that makes the code too complicated.
769 index=skipWhiteSpace(index);
770 UBool eos=index==msg.length();
771 if(eos || msg.charAt(index)==u_rightCurlyBrace) {
772 if(eos==inMessageFormatPattern(nestingLevel)) {
773 setParseError(parseError, start); // Bad plural/select pattern syntax.
774 errorCode=U_PATTERN_SYNTAX_ERROR;
775 return 0;
776 }
777 if(!hasOther) {
778 setParseError(parseError, 0); // Missing 'other' keyword in plural/select pattern.
779 errorCode=U_DEFAULT_KEYWORD_MISSING;
780 return 0;
781 }
782 return index;
783 }
784 int32_t selectorIndex=index;
785 if(argType==UMSGPAT_ARG_TYPE_PLURAL && msg.charAt(selectorIndex)==u_equal) {
786 // explicit-value plural selector: =double
787 index=skipDouble(index+1);
788 int32_t length=index-selectorIndex;
789 if(length==1) {
790 setParseError(parseError, start); // Bad plural/select pattern syntax.
791 errorCode=U_PATTERN_SYNTAX_ERROR;
792 return 0;
793 }
794 if(length>Part::MAX_LENGTH) {
795 setParseError(parseError, selectorIndex); // Argument selector too long.
796 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
797 return 0;
798 }
799 addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode);
800 parseDouble(selectorIndex+1, index, FALSE,
801 parseError, errorCode); // adds ARG_INT or ARG_DOUBLE
802 } else {
803 index=skipIdentifier(index);
804 int32_t length=index-selectorIndex;
805 if(length==0) {
806 setParseError(parseError, start); // Bad plural/select pattern syntax.
807 errorCode=U_PATTERN_SYNTAX_ERROR;
808 return 0;
809 }
810 // Note: The ':' in "offset:" is just beyond the skipIdentifier() range.
811 if( argType==UMSGPAT_ARG_TYPE_PLURAL && length==6 && index<msg.length() &&
812 0==msg.compare(selectorIndex, 7, kOffsetColon, 0, 7)
813 ) {
814 // plural offset, not a selector
815 if(!isEmpty) {
816 // Plural argument 'offset:' (if present) must precede key-message pairs.
817 setParseError(parseError, start);
818 errorCode=U_PATTERN_SYNTAX_ERROR;
819 return 0;
820 }
821 // allow whitespace between offset: and its value
822 int32_t valueIndex=skipWhiteSpace(index+1); // The ':' is at index.
823 index=skipDouble(valueIndex);
824 if(index==valueIndex) {
825 setParseError(parseError, start); // Missing value for plural 'offset:'.
826 errorCode=U_PATTERN_SYNTAX_ERROR;
827 return 0;
828 }
829 if((index-valueIndex)>Part::MAX_LENGTH) {
830 setParseError(parseError, valueIndex); // Plural offset value too long.
831 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
832 return 0;
833 }
834 parseDouble(valueIndex, index, FALSE,
835 parseError, errorCode); // adds ARG_INT or ARG_DOUBLE
836 if(U_FAILURE(errorCode)) {
837 return 0;
838 }
839 isEmpty=FALSE;
840 continue; // no message fragment after the offset
841 } else {
842 // normal selector word
843 if(length>Part::MAX_LENGTH) {
844 setParseError(parseError, selectorIndex); // Argument selector too long.
845 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
846 return 0;
847 }
848 addPart(UMSGPAT_PART_TYPE_ARG_SELECTOR, selectorIndex, length, 0, errorCode);
849 if(0==msg.compare(selectorIndex, length, kOther, 0, 5)) {
850 hasOther=TRUE;
851 }
852 }
853 }
854 if(U_FAILURE(errorCode)) {
855 return 0;
856 }
857
858 // parse the message fragment following the selector
859 index=skipWhiteSpace(index);
860 if(index==msg.length() || msg.charAt(index)!=u_leftCurlyBrace) {
861 setParseError(parseError, selectorIndex); // No message fragment after plural/select selector.
862 errorCode=U_PATTERN_SYNTAX_ERROR;
863 return 0;
864 }
865 index=parseMessage(index, 1, nestingLevel+1, argType, parseError, errorCode);
866 if(U_FAILURE(errorCode)) {
867 return 0;
868 }
869 isEmpty=FALSE;
870 }
871 }
872
873 int32_t
parseArgNumber(const UnicodeString & s,int32_t start,int32_t limit)874 MessagePattern::parseArgNumber(const UnicodeString &s, int32_t start, int32_t limit) {
875 // If the identifier contains only ASCII digits, then it is an argument _number_
876 // and must not have leading zeros (except "0" itself).
877 // Otherwise it is an argument _name_.
878 if(start>=limit) {
879 return UMSGPAT_ARG_NAME_NOT_VALID;
880 }
881 int32_t number;
882 // Defer numeric errors until we know there are only digits.
883 UBool badNumber;
884 UChar c=s.charAt(start++);
885 if(c==0x30) {
886 if(start==limit) {
887 return 0;
888 } else {
889 number=0;
890 badNumber=TRUE; // leading zero
891 }
892 } else if(0x31<=c && c<=0x39) {
893 number=c-0x30;
894 badNumber=FALSE;
895 } else {
896 return UMSGPAT_ARG_NAME_NOT_NUMBER;
897 }
898 while(start<limit) {
899 c=s.charAt(start++);
900 if(0x30<=c && c<=0x39) {
901 if(number>=INT32_MAX/10) {
902 badNumber=TRUE; // overflow
903 }
904 number=number*10+(c-0x30);
905 } else {
906 return UMSGPAT_ARG_NAME_NOT_NUMBER;
907 }
908 }
909 // There are only ASCII digits.
910 if(badNumber) {
911 return UMSGPAT_ARG_NAME_NOT_VALID;
912 } else {
913 return number;
914 }
915 }
916
917 void
parseDouble(int32_t start,int32_t limit,UBool allowInfinity,UParseError * parseError,UErrorCode & errorCode)918 MessagePattern::parseDouble(int32_t start, int32_t limit, UBool allowInfinity,
919 UParseError *parseError, UErrorCode &errorCode) {
920 if(U_FAILURE(errorCode)) {
921 return;
922 }
923 U_ASSERT(start<limit);
924 // fake loop for easy exit and single throw statement
925 for(;;) {
926 // fast path for small integers and infinity
927 int32_t value=0;
928 int32_t isNegative=0; // not boolean so that we can easily add it to value
929 int32_t index=start;
930 UChar c=msg.charAt(index++);
931 if(c==u_minus) {
932 isNegative=1;
933 if(index==limit) {
934 break; // no number
935 }
936 c=msg.charAt(index++);
937 } else if(c==u_plus) {
938 if(index==limit) {
939 break; // no number
940 }
941 c=msg.charAt(index++);
942 }
943 if(c==0x221e) { // infinity
944 if(allowInfinity && index==limit) {
945 double infinity=uprv_getInfinity();
946 addArgDoublePart(
947 isNegative!=0 ? -infinity : infinity,
948 start, limit-start, errorCode);
949 return;
950 } else {
951 break;
952 }
953 }
954 // try to parse the number as a small integer but fall back to a double
955 while('0'<=c && c<='9') {
956 value=value*10+(c-'0');
957 if(value>(Part::MAX_VALUE+isNegative)) {
958 break; // not a small-enough integer
959 }
960 if(index==limit) {
961 addPart(UMSGPAT_PART_TYPE_ARG_INT, start, limit-start,
962 isNegative!=0 ? -value : value, errorCode);
963 return;
964 }
965 c=msg.charAt(index++);
966 }
967 // Let Double.parseDouble() throw a NumberFormatException.
968 char numberChars[128];
969 int32_t capacity=(int32_t)sizeof(numberChars);
970 int32_t length=limit-start;
971 if(length>=capacity) {
972 break; // number too long
973 }
974 msg.extract(start, length, numberChars, capacity, US_INV);
975 if((int32_t)uprv_strlen(numberChars)<length) {
976 break; // contains non-invariant character that was turned into NUL
977 }
978 char *end;
979 double numericValue=uprv_strtod(numberChars, &end);
980 if(end!=(numberChars+length)) {
981 break; // parsing error
982 }
983 addArgDoublePart(numericValue, start, length, errorCode);
984 return;
985 }
986 setParseError(parseError, start /*, limit*/); // Bad syntax for numeric value.
987 errorCode=U_PATTERN_SYNTAX_ERROR;
988 return;
989 }
990
991 int32_t
skipWhiteSpace(int32_t index)992 MessagePattern::skipWhiteSpace(int32_t index) {
993 const UChar *s=msg.getBuffer();
994 int32_t msgLength=msg.length();
995 const UChar *t=PatternProps::skipWhiteSpace(s+index, msgLength-index);
996 return (int32_t)(t-s);
997 }
998
999 int32_t
skipIdentifier(int32_t index)1000 MessagePattern::skipIdentifier(int32_t index) {
1001 const UChar *s=msg.getBuffer();
1002 int32_t msgLength=msg.length();
1003 const UChar *t=PatternProps::skipIdentifier(s+index, msgLength-index);
1004 return (int32_t)(t-s);
1005 }
1006
1007 int32_t
skipDouble(int32_t index)1008 MessagePattern::skipDouble(int32_t index) {
1009 int32_t msgLength=msg.length();
1010 while(index<msgLength) {
1011 UChar c=msg.charAt(index);
1012 // U+221E: Allow the infinity symbol, for ChoiceFormat patterns.
1013 if((c<0x30 && c!=u_plus && c!=u_minus && c!=u_dot) || (c>0x39 && c!=u_e && c!=u_E && c!=0x221e)) {
1014 break;
1015 }
1016 ++index;
1017 }
1018 return index;
1019 }
1020
1021 UBool
isArgTypeChar(UChar32 c)1022 MessagePattern::isArgTypeChar(UChar32 c) {
1023 return (u_a<=c && c<=u_z) || (u_A<=c && c<=u_Z);
1024 }
1025
1026 UBool
isChoice(int32_t index)1027 MessagePattern::isChoice(int32_t index) {
1028 UChar c;
1029 return
1030 ((c=msg.charAt(index++))==u_c || c==u_C) &&
1031 ((c=msg.charAt(index++))==u_h || c==u_H) &&
1032 ((c=msg.charAt(index++))==u_o || c==u_O) &&
1033 ((c=msg.charAt(index++))==u_i || c==u_I) &&
1034 ((c=msg.charAt(index++))==u_c || c==u_C) &&
1035 ((c=msg.charAt(index))==u_e || c==u_E);
1036 }
1037
1038 UBool
isPlural(int32_t index)1039 MessagePattern::isPlural(int32_t index) {
1040 UChar c;
1041 return
1042 ((c=msg.charAt(index++))==u_p || c==u_P) &&
1043 ((c=msg.charAt(index++))==u_l || c==u_L) &&
1044 ((c=msg.charAt(index++))==u_u || c==u_U) &&
1045 ((c=msg.charAt(index++))==u_r || c==u_R) &&
1046 ((c=msg.charAt(index++))==u_a || c==u_A) &&
1047 ((c=msg.charAt(index))==u_l || c==u_L);
1048 }
1049
1050 UBool
isSelect(int32_t index)1051 MessagePattern::isSelect(int32_t index) {
1052 UChar c;
1053 return
1054 ((c=msg.charAt(index++))==u_s || c==u_S) &&
1055 ((c=msg.charAt(index++))==u_e || c==u_E) &&
1056 ((c=msg.charAt(index++))==u_l || c==u_L) &&
1057 ((c=msg.charAt(index++))==u_e || c==u_E) &&
1058 ((c=msg.charAt(index++))==u_c || c==u_C) &&
1059 ((c=msg.charAt(index))==u_t || c==u_T);
1060 }
1061
1062 UBool
inMessageFormatPattern(int32_t nestingLevel)1063 MessagePattern::inMessageFormatPattern(int32_t nestingLevel) {
1064 return nestingLevel>0 || partsList->a[0].type==UMSGPAT_PART_TYPE_MSG_START;
1065 }
1066
1067 UBool
inTopLevelChoiceMessage(int32_t nestingLevel,UMessagePatternArgType parentType)1068 MessagePattern::inTopLevelChoiceMessage(int32_t nestingLevel, UMessagePatternArgType parentType) {
1069 return
1070 nestingLevel==1 &&
1071 parentType==UMSGPAT_ARG_TYPE_CHOICE &&
1072 partsList->a[0].type!=UMSGPAT_PART_TYPE_MSG_START;
1073 }
1074
1075 void
addPart(UMessagePatternPartType type,int32_t index,int32_t length,int32_t value,UErrorCode & errorCode)1076 MessagePattern::addPart(UMessagePatternPartType type, int32_t index, int32_t length,
1077 int32_t value, UErrorCode &errorCode) {
1078 if(partsList->ensureCapacityForOneMore(partsLength, errorCode)) {
1079 Part &part=partsList->a[partsLength++];
1080 part.type=type;
1081 part.index=index;
1082 part.length=(uint16_t)length;
1083 part.value=(int16_t)value;
1084 part.limitPartIndex=0;
1085 }
1086 }
1087
1088 void
addLimitPart(int32_t start,UMessagePatternPartType type,int32_t index,int32_t length,int32_t value,UErrorCode & errorCode)1089 MessagePattern::addLimitPart(int32_t start,
1090 UMessagePatternPartType type, int32_t index, int32_t length,
1091 int32_t value, UErrorCode &errorCode) {
1092 partsList->a[start].limitPartIndex=partsLength;
1093 addPart(type, index, length, value, errorCode);
1094 }
1095
1096 void
addArgDoublePart(double numericValue,int32_t start,int32_t length,UErrorCode & errorCode)1097 MessagePattern::addArgDoublePart(double numericValue, int32_t start, int32_t length,
1098 UErrorCode &errorCode) {
1099 if(U_FAILURE(errorCode)) {
1100 return;
1101 }
1102 int32_t numericIndex=numericValuesLength;
1103 if(numericValuesList==NULL) {
1104 numericValuesList=new MessagePatternDoubleList();
1105 if(numericValuesList==NULL) {
1106 errorCode=U_MEMORY_ALLOCATION_ERROR;
1107 return;
1108 }
1109 } else if(!numericValuesList->ensureCapacityForOneMore(numericValuesLength, errorCode)) {
1110 return;
1111 } else {
1112 if(numericIndex>Part::MAX_VALUE) {
1113 errorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1114 return;
1115 }
1116 }
1117 numericValuesList->a[numericValuesLength++]=numericValue;
1118 addPart(UMSGPAT_PART_TYPE_ARG_DOUBLE, start, length, numericIndex, errorCode);
1119 }
1120
1121 void
setParseError(UParseError * parseError,int32_t index)1122 MessagePattern::setParseError(UParseError *parseError, int32_t index) {
1123 if(parseError==NULL) {
1124 return;
1125 }
1126 parseError->offset=index;
1127
1128 // Set preContext to some of msg before index.
1129 // Avoid splitting a surrogate pair.
1130 int32_t length=index;
1131 if(length>=U_PARSE_CONTEXT_LEN) {
1132 length=U_PARSE_CONTEXT_LEN-1;
1133 if(length>0 && U16_IS_TRAIL(msg[index-length])) {
1134 --length;
1135 }
1136 }
1137 msg.extract(index-length, length, parseError->preContext);
1138 parseError->preContext[length]=0;
1139
1140 // Set postContext to some of msg starting at index.
1141 length=msg.length()-index;
1142 if(length>=U_PARSE_CONTEXT_LEN) {
1143 length=U_PARSE_CONTEXT_LEN-1;
1144 if(length>0 && U16_IS_LEAD(msg[index+length-1])) {
1145 --length;
1146 }
1147 }
1148 msg.extract(index, length, parseError->postContext);
1149 parseError->postContext[length]=0;
1150 }
1151
UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(MessagePattern)1152 UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(MessagePattern)
1153
1154 // MessageImpl ------------------------------------------------------------- ***
1155
1156 void
1157 MessageImpl::appendReducedApostrophes(const UnicodeString &s, int32_t start, int32_t limit,
1158 UnicodeString &sb) {
1159 int32_t doubleApos=-1;
1160 for(;;) {
1161 int32_t i=s.indexOf(u_apos, start);
1162 if(i<0 || i>=limit) {
1163 sb.append(s, start, limit-start);
1164 break;
1165 }
1166 if(i==doubleApos) {
1167 // Double apostrophe at start-1 and start==i, append one.
1168 sb.append(u_apos);
1169 ++start;
1170 doubleApos=-1;
1171 } else {
1172 // Append text between apostrophes and skip this one.
1173 sb.append(s, start, i-start);
1174 doubleApos=start=i+1;
1175 }
1176 }
1177 }
1178
1179 // Ported from second half of ICU4J SelectFormat.format(String).
1180 UnicodeString &
appendSubMessageWithoutSkipSyntax(const MessagePattern & msgPattern,int32_t msgStart,UnicodeString & result)1181 MessageImpl::appendSubMessageWithoutSkipSyntax(const MessagePattern &msgPattern,
1182 int32_t msgStart,
1183 UnicodeString &result) {
1184 const UnicodeString &msgString=msgPattern.getPatternString();
1185 int32_t prevIndex=msgPattern.getPart(msgStart).getLimit();
1186 for(int32_t i=msgStart;;) {
1187 const MessagePattern::Part &part=msgPattern.getPart(++i);
1188 UMessagePatternPartType type=part.getType();
1189 int32_t index=part.getIndex();
1190 if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
1191 return result.append(msgString, prevIndex, index-prevIndex);
1192 } else if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
1193 result.append(msgString, prevIndex, index-prevIndex);
1194 prevIndex=part.getLimit();
1195 } else if(type==UMSGPAT_PART_TYPE_ARG_START) {
1196 result.append(msgString, prevIndex, index-prevIndex);
1197 prevIndex=index;
1198 i=msgPattern.getLimitPartIndex(i);
1199 index=msgPattern.getPart(i).getLimit();
1200 appendReducedApostrophes(msgString, prevIndex, index, result);
1201 prevIndex=index;
1202 }
1203 }
1204 }
1205
1206 U_NAMESPACE_END
1207
1208 #endif // !UCONFIG_NO_FORMATTING
1209