2 ******************************************************************************
3 * Copyright (C) 2014, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 ******************************************************************************
6 * simplepatternformatter.cpp
8 #include "simplepatternformatter.h"
14 static UBool
isInvalidArray(const void *array
, int32_t size
) {
15 return (size
< 0 || (size
> 0 && array
== NULL
));
18 typedef enum SimplePatternFormatterCompileState
{
22 } SimplePatternFormatterCompileState
;
24 // Handles parsing placeholders in the pattern string, e.g {4} or {35}
25 class SimplePatternFormatterIdBuilder
{
27 SimplePatternFormatterIdBuilder() : id(0), idLen(0) { }
28 ~SimplePatternFormatterIdBuilder() { }
30 // Resets so that this object has seen no placeholder ID.
31 void reset() { id
= 0; idLen
= 0; }
33 // Returns the numeric placeholder ID parsed so far
34 int32_t getId() const { return id
; }
36 // Appends the numeric placeholder ID parsed so far back to a
37 // UChar buffer. Used to recover if parser using this object finds
38 // no closing curly brace.
39 void appendTo(UChar
*buffer
, int32_t *len
) const;
41 // Returns true if this object has seen a placeholder ID.
42 UBool
isValid() const { return (idLen
> 0); }
44 // Processes a single digit character. Pattern string parser calls this
45 // as it processes digits after an opening curly brace.
50 SimplePatternFormatterIdBuilder(
51 const SimplePatternFormatterIdBuilder
&other
);
52 SimplePatternFormatterIdBuilder
&operator=(
53 const SimplePatternFormatterIdBuilder
&other
);
56 void SimplePatternFormatterIdBuilder::appendTo(
57 UChar
*buffer
, int32_t *len
) const {
58 int32_t origLen
= *len
;
60 for (int32_t i
= origLen
+ idLen
- 1; i
>= origLen
; i
--) {
61 int32_t digit
= kId
% 10;
62 buffer
[i
] = digit
+ 0x30;
65 *len
= origLen
+ idLen
;
68 void SimplePatternFormatterIdBuilder::add(UChar ch
) {
69 id
= id
* 10 + (ch
- 0x30);
73 // Represents placeholder values.
74 class SimplePatternFormatterPlaceholderValues
: public UMemory
{
76 SimplePatternFormatterPlaceholderValues(
77 const UnicodeString
* const *values
,
80 // Returns TRUE if appendTo value is at any index besides exceptIndex.
81 UBool
isAppendToInAnyIndexExcept(
82 const UnicodeString
&appendTo
, int32_t exceptIndex
) const;
84 // For each appendTo value, stores the snapshot of it in its place.
85 void snapshotAppendTo(const UnicodeString
&appendTo
);
87 // Returns the placeholder value at index. No range checking performed.
88 // Returned reference is valid for as long as this object exists.
89 const UnicodeString
&get(int32_t index
) const;
91 const UnicodeString
* const *fValues
;
93 const UnicodeString
*fAppendTo
;
94 UnicodeString fAppendToCopy
;
95 SimplePatternFormatterPlaceholderValues(
96 const SimplePatternFormatterPlaceholderValues
&);
97 SimplePatternFormatterPlaceholderValues
&operator=(
98 const SimplePatternFormatterPlaceholderValues
&);
101 SimplePatternFormatterPlaceholderValues::SimplePatternFormatterPlaceholderValues(
102 const UnicodeString
* const *values
,
105 fValuesCount(valuesCount
),
110 UBool
SimplePatternFormatterPlaceholderValues::isAppendToInAnyIndexExcept(
111 const UnicodeString
&appendTo
, int32_t exceptIndex
) const {
112 for (int32_t i
= 0; i
< fValuesCount
; ++i
) {
113 if (i
!= exceptIndex
&& fValues
[i
] == &appendTo
) {
120 void SimplePatternFormatterPlaceholderValues::snapshotAppendTo(
121 const UnicodeString
&appendTo
) {
122 fAppendTo
= &appendTo
;
123 fAppendToCopy
= appendTo
;
126 const UnicodeString
&SimplePatternFormatterPlaceholderValues::get(
127 int32_t index
) const {
128 if (fAppendTo
== NULL
|| fAppendTo
!= fValues
[index
]) {
129 return *fValues
[index
];
131 return fAppendToCopy
;
134 SimplePatternFormatter::SimplePatternFormatter() :
139 firstPlaceholderReused(FALSE
) {
142 SimplePatternFormatter::SimplePatternFormatter(const UnicodeString
&pattern
) :
147 firstPlaceholderReused(FALSE
) {
148 UErrorCode status
= U_ZERO_ERROR
;
149 compile(pattern
, status
);
152 SimplePatternFormatter::SimplePatternFormatter(
153 const SimplePatternFormatter
&other
) :
154 noPlaceholders(other
.noPlaceholders
),
157 placeholderCount(other
.placeholderCount
),
158 firstPlaceholderReused(other
.firstPlaceholderReused
) {
159 placeholderSize
= ensureCapacity(other
.placeholderSize
);
161 placeholders
.getAlias(),
162 other
.placeholders
.getAlias(),
163 placeholderSize
* sizeof(PlaceholderInfo
));
166 SimplePatternFormatter
&SimplePatternFormatter::operator=(
167 const SimplePatternFormatter
& other
) {
168 if (this == &other
) {
171 noPlaceholders
= other
.noPlaceholders
;
172 placeholderSize
= ensureCapacity(other
.placeholderSize
);
173 placeholderCount
= other
.placeholderCount
;
174 firstPlaceholderReused
= other
.firstPlaceholderReused
;
176 placeholders
.getAlias(),
177 other
.placeholders
.getAlias(),
178 placeholderSize
* sizeof(PlaceholderInfo
));
182 SimplePatternFormatter::~SimplePatternFormatter() {
185 UBool
SimplePatternFormatter::compile(
186 const UnicodeString
&pattern
, UErrorCode
&status
) {
187 if (U_FAILURE(status
)) {
190 const UChar
*patternBuffer
= pattern
.getBuffer();
191 int32_t patternLength
= pattern
.length();
192 UChar
*buffer
= noPlaceholders
.getBuffer(patternLength
);
195 placeholderCount
= 0;
196 SimplePatternFormatterCompileState state
= INIT
;
197 SimplePatternFormatterIdBuilder idBuilder
;
198 for (int32_t i
= 0; i
< patternLength
; ++i
) {
199 UChar ch
= patternBuffer
[i
];
204 } else if (ch
== 0x7B) {
213 buffer
[len
++] = 0x27;
214 } else if (ch
== 0x7B) {
215 buffer
[len
++] = 0x7B;
217 buffer
[len
++] = 0x27;
223 if (ch
>= 0x30 && ch
<= 0x39) {
225 } else if (ch
== 0x7D && idBuilder
.isValid()) {
226 if (!addPlaceholder(idBuilder
.getId(), len
)) {
227 status
= U_MEMORY_ALLOCATION_ERROR
;
232 buffer
[len
++] = 0x7B;
233 idBuilder
.appendTo(buffer
, &len
);
247 buffer
[len
++] = 0x27;
250 buffer
[len
++] = 0X7B;
251 idBuilder
.appendTo(buffer
, &len
);
257 noPlaceholders
.releaseBuffer(len
);
261 UnicodeString
& SimplePatternFormatter::format(
262 const UnicodeString
&arg0
,
263 UnicodeString
&appendTo
,
264 UErrorCode
&status
) const {
265 const UnicodeString
*params
[] = {&arg0
};
266 return formatAndAppend(
268 UPRV_LENGTHOF(params
),
275 UnicodeString
& SimplePatternFormatter::format(
276 const UnicodeString
&arg0
,
277 const UnicodeString
&arg1
,
278 UnicodeString
&appendTo
,
279 UErrorCode
&status
) const {
280 const UnicodeString
*params
[] = {&arg0
, &arg1
};
281 return formatAndAppend(
283 UPRV_LENGTHOF(params
),
290 UnicodeString
& SimplePatternFormatter::format(
291 const UnicodeString
&arg0
,
292 const UnicodeString
&arg1
,
293 const UnicodeString
&arg2
,
294 UnicodeString
&appendTo
,
295 UErrorCode
&status
) const {
296 const UnicodeString
*params
[] = {&arg0
, &arg1
, &arg2
};
297 return formatAndAppend(
299 UPRV_LENGTHOF(params
),
306 static void updatePlaceholderOffset(
307 int32_t placeholderId
,
308 int32_t placeholderOffset
,
309 int32_t *offsetArray
,
310 int32_t offsetArrayLength
) {
311 if (placeholderId
< offsetArrayLength
) {
312 offsetArray
[placeholderId
] = placeholderOffset
;
316 static void appendRange(
317 const UnicodeString
&src
,
320 UnicodeString
&dest
) {
321 // This check improves performance significantly.
325 dest
.append(src
, start
, end
- start
);
328 UnicodeString
& SimplePatternFormatter::formatAndAppend(
329 const UnicodeString
* const *placeholderValues
,
330 int32_t placeholderValueCount
,
331 UnicodeString
&appendTo
,
332 int32_t *offsetArray
,
333 int32_t offsetArrayLength
,
334 UErrorCode
&status
) const {
335 if (U_FAILURE(status
)) {
338 if (isInvalidArray(placeholderValues
, placeholderValueCount
)
339 || isInvalidArray(offsetArray
, offsetArrayLength
)) {
340 status
= U_ILLEGAL_ARGUMENT_ERROR
;
343 if (placeholderValueCount
< placeholderCount
) {
344 status
= U_ILLEGAL_ARGUMENT_ERROR
;
348 // Since we are disallowing parameter values that are the same as
349 // appendTo, we have to check all placeholderValues as opposed to
350 // the first placeholderCount placeholder values.
351 SimplePatternFormatterPlaceholderValues
values(
352 placeholderValues
, placeholderValueCount
);
353 if (values
.isAppendToInAnyIndexExcept(appendTo
, -1)) {
354 status
= U_ILLEGAL_ARGUMENT_ERROR
;
357 return formatAndAppend(
364 UnicodeString
& SimplePatternFormatter::formatAndReplace(
365 const UnicodeString
* const *placeholderValues
,
366 int32_t placeholderValueCount
,
367 UnicodeString
&result
,
368 int32_t *offsetArray
,
369 int32_t offsetArrayLength
,
370 UErrorCode
&status
) const {
371 if (U_FAILURE(status
)) {
374 if (isInvalidArray(placeholderValues
, placeholderValueCount
)
375 || isInvalidArray(offsetArray
, offsetArrayLength
)) {
376 status
= U_ILLEGAL_ARGUMENT_ERROR
;
379 if (placeholderValueCount
< placeholderCount
) {
380 status
= U_ILLEGAL_ARGUMENT_ERROR
;
383 SimplePatternFormatterPlaceholderValues
values(
384 placeholderValues
, placeholderCount
);
385 int32_t placeholderAtStart
= getUniquePlaceholderAtStart();
387 // If pattern starts with a unique placeholder and that placeholder
388 // value is result, we may be able to optimize by just appending to result.
389 if (placeholderAtStart
>= 0
390 && placeholderValues
[placeholderAtStart
] == &result
) {
392 // If result is the value for other placeholders, call off optimization.
393 if (values
.isAppendToInAnyIndexExcept(result
, placeholderAtStart
)) {
394 values
.snapshotAppendTo(result
);
396 return formatAndAppend(
403 // Otherwise we can optimize
410 // We have to make the offset for the placeholderAtStart
411 // placeholder be 0. Otherwise it would be the length of the
412 // previous value of result.
413 if (offsetArrayLength
> placeholderAtStart
) {
414 offsetArray
[placeholderAtStart
] = 0;
418 if (values
.isAppendToInAnyIndexExcept(result
, -1)) {
419 values
.snapshotAppendTo(result
);
422 return formatAndAppend(
429 UnicodeString
& SimplePatternFormatter::formatAndAppend(
430 const SimplePatternFormatterPlaceholderValues
&values
,
431 UnicodeString
&appendTo
,
432 int32_t *offsetArray
,
433 int32_t offsetArrayLength
) const {
434 for (int32_t i
= 0; i
< offsetArrayLength
; ++i
) {
437 if (placeholderSize
== 0) {
438 appendTo
.append(noPlaceholders
);
444 placeholders
[0].offset
,
446 updatePlaceholderOffset(
451 const UnicodeString
*placeholderValue
= &values
.get(placeholders
[0].id
);
452 if (placeholderValue
!= &appendTo
) {
453 appendTo
.append(*placeholderValue
);
455 for (int32_t i
= 1; i
< placeholderSize
; ++i
) {
458 placeholders
[i
- 1].offset
,
459 placeholders
[i
].offset
,
461 updatePlaceholderOffset(
466 placeholderValue
= &values
.get(placeholders
[i
].id
);
467 if (placeholderValue
!= &appendTo
) {
468 appendTo
.append(*placeholderValue
);
473 placeholders
[placeholderSize
- 1].offset
,
474 noPlaceholders
.length(),
479 int32_t SimplePatternFormatter::getUniquePlaceholderAtStart() const {
480 if (placeholderSize
== 0
481 || firstPlaceholderReused
|| placeholders
[0].offset
!= 0) {
484 return placeholders
[0].id
;
487 int32_t SimplePatternFormatter::ensureCapacity(
488 int32_t desiredCapacity
, int32_t allocationSize
) {
489 if (allocationSize
< desiredCapacity
) {
490 allocationSize
= desiredCapacity
;
492 if (desiredCapacity
<= placeholders
.getCapacity()) {
493 return desiredCapacity
;
495 // allocate new buffer
496 if (placeholders
.resize(allocationSize
, placeholderSize
) == NULL
) {
497 return placeholders
.getCapacity();
499 return desiredCapacity
;
502 UBool
SimplePatternFormatter::addPlaceholder(int32_t id
, int32_t offset
) {
503 if (ensureCapacity(placeholderSize
+ 1, 2 * placeholderSize
) < placeholderSize
+ 1) {
507 PlaceholderInfo
*placeholderEnd
= &placeholders
[placeholderSize
- 1];
508 placeholderEnd
->offset
= offset
;
509 placeholderEnd
->id
= id
;
510 if (id
>= placeholderCount
) {
511 placeholderCount
= id
+ 1;
513 if (placeholderSize
> 1
514 && placeholders
[placeholderSize
- 1].id
== placeholders
[0].id
) {
515 firstPlaceholderReused
= TRUE
;