Coverage Report

Created: 2025-06-24 06:43

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