Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: patches/measure_format.patch

Issue 1621943002: ICU 56 step 4: Apply post-56 fixes for measure/date format (Closed) Base URL: https://chromium.googlesource.com/chromium/deps/icu.git@56goog
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « patches/locid.patch ('k') | patches/mutex.patch » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 diff --git a/source/common/Makefile.in b/source/common/Makefile.in
2 index 6dd4c4d..b187af6 100644
3 --- a/source/common/Makefile.in
4 +++ b/source/common/Makefile.in
5 @@ -84,7 +84,7 @@ uhash.o uhash_us.o uenum.o ustrenum.o uvector.o ustack.o uvect r32.o uvectr64.o \
6 ucnv.o ucnv_bld.o ucnv_cnv.o ucnv_io.o ucnv_cb.o ucnv_err.o ucnvlat1.o \
7 ucnv_u7.o ucnv_u8.o ucnv_u16.o ucnv_u32.o ucnvscsu.o ucnvbocu.o \
8 ucnv_ext.o ucnvmbcs.o ucnv2022.o ucnvhz.o ucnv_lmb.o ucnvisci.o ucnvdisp.o ucnv _set.o ucnv_ct.o \
9 -uresbund.o ures_cnv.o uresdata.o resbund.o resbund_cnv.o \
10 +resource.o uresbund.o ures_cnv.o uresdata.o resbund.o resbund_cnv.o \
11 messagepattern.o ucat.o locmap.o uloc.o locid.o locutil.o locavailable.o locdis pnames.o loclikely.o locresdata.o \
12 bytestream.o stringpiece.o \
13 stringtriebuilder.o bytestriebuilder.o \
14 diff --git a/source/common/common.vcxproj b/source/common/common.vcxproj
15 index 24bf4de..18ca9a3 100644
16 --- a/source/common/common.vcxproj
17 +++ b/source/common/common.vcxproj
18 @@ -379,7 +379,8 @@
19 <ClCompile Include="uloc_tag.c" />
20 <ClCompile Include="ures_cnv.c" />
21 <ClCompile Include="uresbund.cpp" />
22 - <ClCompile Include="uresdata.c" />
23 + <ClCompile Include="uresdata.cpp" />
24 + <ClCompile Include="resource.cpp" />
25 <ClCompile Include="caniter.cpp">
26 </ClCompile>
27 <ClCompile Include="filterednormalizer2.cpp" />
28 @@ -1135,6 +1136,7 @@
29 <ClInclude Include="uresdata.h" />
30 <ClInclude Include="uresimp.h" />
31 <ClInclude Include="ureslocs.h" />
32 + <ClInclude Include="resource.h" />
33 <CustomBuild Include="unicode\caniter.h">
34 <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy " %(FullPath)" ..\..\include\unicode
35 </Command>
36 diff --git a/source/common/common.vcxproj.filters b/source/common/common.vcxproj .filters
37 index 01b0625..4a4954b 100644
38 --- a/source/common/common.vcxproj.filters
39 +++ b/source/common/common.vcxproj.filters
40 @@ -343,7 +343,10 @@
41 <ClCompile Include="uresbund.cpp">
42 <Filter>locales &amp; resources</Filter>
43 </ClCompile>
44 - <ClCompile Include="uresdata.c">
45 + <ClCompile Include="uresdata.cpp">
46 + <Filter>locales &amp; resources</Filter>
47 + </ClCompile>
48 + <ClCompile Include="resource.cpp">
49 <Filter>locales &amp; resources</Filter>
50 </ClCompile>
51 <ClCompile Include="caniter.cpp">
52 @@ -769,6 +772,9 @@
53 <ClInclude Include="uresdata.h">
54 <Filter>locales &amp; resources</Filter>
55 </ClInclude>
56 + <ClInclude Include="resource.h">
57 + <Filter>locales &amp; resources</Filter>
58 + </ClInclude>
59 <ClInclude Include="uresimp.h">
60 <Filter>locales &amp; resources</Filter>
61 </ClInclude>
62 diff --git a/source/common/listformatter.cpp b/source/common/listformatter.cpp
63 index 6dbf6c5..4941fa2 100644
64 --- a/source/common/listformatter.cpp
65 +++ b/source/common/listformatter.cpp
66 @@ -1,7 +1,7 @@
67 /*
68 *******************************************************************************
69 *
70 -* Copyright (C) 2013-2014, International Business Machines
71 +* Copyright (C) 2013-2015, International Business Machines
72 * Corporation and others. All Rights Reserved.
73 *
74 *******************************************************************************
75 @@ -36,11 +36,12 @@ ListFormatInternal(
76 const UnicodeString& two,
77 const UnicodeString& start,
78 const UnicodeString& middle,
79 - const UnicodeString& end) :
80 - twoPattern(two),
81 - startPattern(start),
82 - middlePattern(middle),
83 - endPattern(end) {}
84 + const UnicodeString& end,
85 + UErrorCode &errorCode) :
86 + twoPattern(two, 2, 2, errorCode),
87 + startPattern(start, 2, 2, errorCode),
88 + middlePattern(middle, 2, 2, errorCode),
89 + endPattern(end, 2, 2, errorCode) {}
90
91 ListFormatInternal(const ListFormatData &data) :
92 twoPattern(data.twoPattern),
93 @@ -191,11 +192,15 @@ static ListFormatInternal* loadListFormatInternal(
94 if (U_FAILURE(errorCode)) {
95 return NULL;
96 }
97 - ListFormatInternal* result = new ListFormatInternal(two, start, middle, end );
98 + ListFormatInternal* result = new ListFormatInternal(two, start, middle, end , errorCode);
99 if (result == NULL) {
100 errorCode = U_MEMORY_ALLOCATION_ERROR;
101 return NULL;
102 }
103 + if (U_FAILURE(errorCode)) {
104 + delete result;
105 + return NULL;
106 + }
107 return result;
108 }
109
110 diff --git a/source/common/resource.cpp b/source/common/resource.cpp
111 new file mode 100644
112 index 0000000..7a4c418
113 --- /dev/null
114 +++ b/source/common/resource.cpp
115 @@ -0,0 +1,60 @@
116 +/*
117 +*******************************************************************************
118 +* Copyright (C) 2015, International Business Machines
119 +* Corporation and others. All Rights Reserved.
120 +*******************************************************************************
121 +* resource.cpp
122 +*
123 +* created on: 2015nov04
124 +* created by: Markus W. Scherer
125 +*/
126 +
127 +#include "resource.h"
128 +
129 +#include "unicode/utypes.h"
130 +#include "unicode/uobject.h"
131 +#include "unicode/ures.h"
132 +
133 +U_NAMESPACE_BEGIN
134 +
135 +ResourceValue::~ResourceValue() {}
136 +
137 +
138 +ResourceArraySink::~ResourceArraySink() {}
139 +
140 +void ResourceArraySink::put(
141 + int32_t /*index*/, const ResourceValue & /*value*/, UErrorCode & /*erro rCode*/) {}
142 +
143 +ResourceArraySink *ResourceArraySink::getOrCreateArraySink(
144 + int32_t /*index*/, int32_t /*size*/, UErrorCode & /*errorCode*/) {
145 + return NULL;
146 +}
147 +
148 +ResourceTableSink *ResourceArraySink::getOrCreateTableSink(
149 + int32_t /*index*/, int32_t /*initialSize*/, UErrorCode & /*errorCode*/) {
150 + return NULL;
151 +}
152 +
153 +void ResourceArraySink::leave(UErrorCode & /*errorCode*/) {}
154 +
155 +
156 +ResourceTableSink::~ResourceTableSink() {}
157 +
158 +void ResourceTableSink::put(
159 + const char * /*key*/, const ResourceValue & /*value*/, UErrorCode & /*e rrorCode*/) {}
160 +
161 +void ResourceTableSink::putNoFallback(const char * /*key*/, UErrorCode & /*erro rCode*/) {}
162 +
163 +ResourceArraySink *ResourceTableSink::getOrCreateArraySink(
164 + const char * /*key*/, int32_t /*size*/, UErrorCode & /*errorCode*/) {
165 + return NULL;
166 +}
167 +
168 +ResourceTableSink *ResourceTableSink::getOrCreateTableSink(
169 + const char * /*key*/, int32_t /*initialSize*/, UErrorCode & /*errorCode */) {
170 + return NULL;
171 +}
172 +
173 +void ResourceTableSink::leave(UErrorCode & /*errorCode*/) {}
174 +
175 +U_NAMESPACE_END
176 diff --git a/source/common/resource.h b/source/common/resource.h
177 new file mode 100644
178 index 0000000..042e298
179 --- /dev/null
180 +++ b/source/common/resource.h
181 @@ -0,0 +1,248 @@
182 +/*
183 +*******************************************************************************
184 +* Copyright (C) 2015, International Business Machines
185 +* Corporation and others. All Rights Reserved.
186 +*******************************************************************************
187 +* resource.h
188 +*
189 +* created on: 2015nov04
190 +* created by: Markus W. Scherer
191 +*/
192 +
193 +#ifndef __URESOURCE_H__
194 +#define __URESOURCE_H__
195 +
196 +/**
197 + * \file
198 + * \brief ICU resource bundle key and value types.
199 + */
200 +
201 +// Note: Ported from ICU4J class UResource and its nested classes,
202 +// but the C++ classes are separate, not nested.
203 +
204 +// We use the Resource prefix for C++ classes, as usual.
205 +// The UResource prefix would be used for C types.
206 +
207 +#include "unicode/utypes.h"
208 +#include "unicode/unistr.h"
209 +#include "unicode/ures.h"
210 +
211 +U_NAMESPACE_BEGIN
212 +
213 +class ResourceTableSink;
214 +
215 +// Note: In C++, we use const char * pointers for keys,
216 +// rather than an abstraction like Java UResource.Key.
217 +
218 +/**
219 + * Represents a resource bundle item's value.
220 + * Avoids object creations as much as possible.
221 + * Mutable, not thread-safe.
222 + */
223 +class U_COMMON_API ResourceValue : public UObject {
224 +public:
225 + virtual ~ResourceValue();
226 +
227 + /**
228 + * @return ICU resource type, for example, URES_STRING
229 + */
230 + virtual UResType getType() const = 0;
231 +
232 + /**
233 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a string resource.
234 + *
235 + * @see ures_getString()
236 + */
237 + virtual const UChar *getString(int32_t &length, UErrorCode &errorCode) cons t = 0;
238 +
239 + inline UnicodeString getUnicodeString(UErrorCode &errorCode) const {
240 + int32_t len = 0;
241 + const UChar *r = getString(len, errorCode);
242 + return UnicodeString(TRUE, r, len);
243 + }
244 +
245 + /**
246 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an alias resource.
247 + */
248 + virtual const UChar *getAliasString(int32_t &length, UErrorCode &errorCode) const = 0;
249 +
250 + inline UnicodeString getAliasUnicodeString(UErrorCode &errorCode) const {
251 + int32_t len = 0;
252 + const UChar *r = getAliasString(len, errorCode);
253 + return UnicodeString(TRUE, r, len);
254 + }
255 +
256 + /**
257 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource.
258 + *
259 + * @see ures_getInt()
260 + */
261 + virtual int32_t getInt(UErrorCode &errorCode) const = 0;
262 +
263 + /**
264 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an integer resource.
265 + *
266 + * @see ures_getUInt()
267 + */
268 + virtual uint32_t getUInt(UErrorCode &errorCode) const = 0;
269 +
270 + /**
271 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not an intvector resource.
272 + *
273 + * @see ures_getIntVector()
274 + */
275 + virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const = 0;
276 +
277 + /**
278 + * Sets U_RESOURCE_TYPE_MISMATCH if this is not a binary-blob resource.
279 + *
280 + * @see ures_getBinary()
281 + */
282 + virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) co nst = 0;
283 +
284 +protected:
285 + ResourceValue() {}
286 +
287 +private:
288 + ResourceValue(const ResourceValue &); // no copy constructor
289 + ResourceValue &operator=(const ResourceValue &); // no assignment operator
290 +};
291 +
292 +/**
293 + * Sink for ICU resource array contents.
294 + * The base class does nothing.
295 + *
296 + * Nested arrays and tables are stored as nested sinks,
297 + * never put() as ResourceValue items.
298 + */
299 +class U_COMMON_API ResourceArraySink : public UObject {
300 +public:
301 + ResourceArraySink() {}
302 + virtual ~ResourceArraySink();
303 +
304 + /**
305 + * Adds a value from a resource array.
306 + *
307 + * @param index of the resource array item
308 + * @param value resource value
309 + */
310 + virtual void put(int32_t index, const ResourceValue &value, UErrorCode &err orCode);
311 +
312 + /**
313 + * Returns a nested resource array at the array index as another sink.
314 + * Creates the sink if none exists for the key.
315 + * Returns NULL if nested arrays are not supported.
316 + * The default implementation always returns NULL.
317 + *
318 + * This sink (not the caller) owns the nested sink.
319 + *
320 + * @param index of the resource array item
321 + * @param size number of array items
322 + * @return nested-array sink, or NULL
323 + */
324 + virtual ResourceArraySink *getOrCreateArraySink(
325 + int32_t index, int32_t size, UErrorCode &errorCode);
326 +
327 + /**
328 + * Returns a nested resource table at the array index as another sink.
329 + * Creates the sink if none exists for the key.
330 + * Returns NULL if nested tables are not supported.
331 + * The default implementation always returns NULL.
332 + *
333 + * This sink (not the caller) owns the nested sink.
334 + *
335 + * @param index of the resource array item
336 + * @param initialSize size hint for creating the sink if necessary
337 + * @return nested-table sink, or NULL
338 + */
339 + virtual ResourceTableSink *getOrCreateTableSink(
340 + int32_t index, int32_t initialSize, UErrorCode &errorCode);
341 +
342 + /**
343 + * "Leaves" the array.
344 + * Indicates that all of the resources and sub-resources of the current arr ay
345 + * have been enumerated.
346 + */
347 + virtual void leave(UErrorCode &errorCode);
348 +
349 +private:
350 + ResourceArraySink(const ResourceArraySink &); // no copy constructor
351 + ResourceArraySink &operator=(const ResourceArraySink &); // no assignment operator
352 +};
353 +
354 +/**
355 + * Sink for ICU resource table contents.
356 + * The base class does nothing.
357 + *
358 + * Nested arrays and tables are stored as nested sinks,
359 + * never put() as ResourceValue items.
360 + */
361 +class U_COMMON_API ResourceTableSink : public UObject {
362 +public:
363 + ResourceTableSink() {}
364 + virtual ~ResourceTableSink();
365 +
366 + /**
367 + * Adds a key-value pair from a resource table.
368 + *
369 + * @param key resource key string
370 + * @param value resource value
371 + */
372 + virtual void put(const char *key, const ResourceValue &value, UErrorCode &e rrorCode);
373 +
374 + /**
375 + * Adds a no-fallback/no-inheritance marker for this key.
376 + * Used for CLDR no-fallback data values of (three empty-set symbols)=={220 5, 2205, 2205}
377 + * when enumerating tables with fallback from the specific resource bundle to root.
378 + *
379 + * The default implementation does nothing.
380 + *
381 + * @param key to be removed
382 + */
383 + virtual void putNoFallback(const char *key, UErrorCode &errorCode);
384 +
385 + /**
386 + * Returns a nested resource array for the key as another sink.
387 + * Creates the sink if none exists for the key.
388 + * Returns NULL if nested arrays are not supported.
389 + * The default implementation always returns NULL.
390 + *
391 + * This sink (not the caller) owns the nested sink.
392 + *
393 + * @param key resource key string
394 + * @param size number of array items
395 + * @return nested-array sink, or NULL
396 + */
397 + virtual ResourceArraySink *getOrCreateArraySink(
398 + const char *key, int32_t size, UErrorCode &errorCode);
399 +
400 + /**
401 + * Returns a nested resource table for the key as another sink.
402 + * Creates the sink if none exists for the key.
403 + * Returns NULL if nested tables are not supported.
404 + * The default implementation always returns NULL.
405 + *
406 + * This sink (not the caller) owns the nested sink.
407 + *
408 + * @param key resource key string
409 + * @param initialSize size hint for creating the sink if necessary
410 + * @return nested-table sink, or NULL
411 + */
412 + virtual ResourceTableSink *getOrCreateTableSink(
413 + const char *key, int32_t initialSize, UErrorCode &errorCode);
414 +
415 + /**
416 + * "Leaves" the table.
417 + * Indicates that all of the resources and sub-resources of the current tab le
418 + * have been enumerated.
419 + */
420 + virtual void leave(UErrorCode &errorCode);
421 +
422 +private:
423 + ResourceTableSink(const ResourceTableSink &); // no copy constructor
424 + ResourceTableSink &operator=(const ResourceTableSink &); // no assignment operator
425 +};
426 +
427 +U_NAMESPACE_END
428 +
429 +#endif
430 diff --git a/source/common/simplepatternformatter.cpp b/source/common/simplepatt ernformatter.cpp
431 index 0cac2ec..abaaea9 100644
432 --- a/source/common/simplepatternformatter.cpp
433 +++ b/source/common/simplepatternformatter.cpp
434 @@ -1,6 +1,6 @@
435 /*
436 ******************************************************************************
437 -* Copyright (C) 2014, International Business Machines
438 +* Copyright (C) 2014-2015, International Business Machines
439 * Corporation and others. All Rights Reserved.
440 ******************************************************************************
441 * simplepatternformatter.cpp
442 @@ -149,6 +149,17 @@ SimplePatternFormatter::SimplePatternFormatter(const Unicod eString &pattern) :
443 compile(pattern, status);
444 }
445
446 +SimplePatternFormatter::SimplePatternFormatter(const UnicodeString &pattern,
447 + int32_t min, int32_t max,
448 + UErrorCode &errorCode)
449 + : noPlaceholders(),
450 + placeholders(),
451 + placeholderSize(0),
452 + placeholderCount(0),
453 + firstPlaceholderReused(FALSE) {
454 + compileMinMaxPlaceholders(pattern, min, max, errorCode);
455 +}
456 +
457 SimplePatternFormatter::SimplePatternFormatter(
458 const SimplePatternFormatter &other) :
459 noPlaceholders(other.noPlaceholders),
460 @@ -182,8 +193,10 @@ SimplePatternFormatter &SimplePatternFormatter::operator=(
461 SimplePatternFormatter::~SimplePatternFormatter() {
462 }
463
464 -UBool SimplePatternFormatter::compile(
465 - const UnicodeString &pattern, UErrorCode &status) {
466 +UBool SimplePatternFormatter::compileMinMaxPlaceholders(
467 + const UnicodeString &pattern,
468 + int32_t min, int32_t max,
469 + UErrorCode &status) {
470 if (U_FAILURE(status)) {
471 return FALSE;
472 }
473 @@ -224,6 +237,7 @@ UBool SimplePatternFormatter::compile(
474 idBuilder.add(ch);
475 } else if (ch == 0x7D && idBuilder.isValid()) {
476 if (!addPlaceholder(idBuilder.getId(), len)) {
477 + noPlaceholders.releaseBuffer(0);
478 status = U_MEMORY_ALLOCATION_ERROR;
479 return FALSE;
480 }
481 @@ -255,6 +269,10 @@ UBool SimplePatternFormatter::compile(
482 break;
483 }
484 noPlaceholders.releaseBuffer(len);
485 + if (placeholderCount < min || max < placeholderCount) {
486 + status = U_ILLEGAL_ARGUMENT_ERROR;
487 + return FALSE;
488 + }
489 return TRUE;
490 }
491
492 diff --git a/source/common/simplepatternformatter.h b/source/common/simplepatter nformatter.h
493 index 6740dc9..782a29c 100644
494 --- a/source/common/simplepatternformatter.h
495 +++ b/source/common/simplepatternformatter.h
496 @@ -1,6 +1,6 @@
497 /*
498 ******************************************************************************
499 -* Copyright (C) 2014, International Business Machines
500 +* Copyright (C) 2014-2015, International Business Machines
501 * Corporation and others. All Rights Reserved.
502 ******************************************************************************
503 * simplepatternformatter.h
504 @@ -52,12 +52,22 @@ public:
505 SimplePatternFormatter();
506
507 /**
508 - * Construct from a pattern. Will never fail if pattern has three or
509 + * Constructs from a pattern. Will never fail if pattern has three or
510 * fewer placeholders in it.
511 */
512 explicit SimplePatternFormatter(const UnicodeString& pattern);
513
514 /**
515 + * Constructs from a pattern. Will never fail if pattern has three or
516 + * fewer placeholders in it.
517 + *
518 + * @param min The pattern must have at least this many placeholders.
519 + * @param max The pattern must have at most this many placeholders.
520 + */
521 + SimplePatternFormatter(const UnicodeString& pattern, int32_t min, int32_t m ax,
522 + UErrorCode &errorCode);
523 +
524 + /**
525 * Copy constructor.
526 */
527 SimplePatternFormatter(const SimplePatternFormatter& other);
528 @@ -79,7 +89,22 @@ public:
529 * there are three or fewer placeholders in pattern. May fail with
530 * U_MEMORY_ALLOCATION_ERROR if there are more than three placeholders.
531 */
532 - UBool compile(const UnicodeString &pattern, UErrorCode &status);
533 + UBool compile(const UnicodeString &pattern, UErrorCode &status) {
534 + return compileMinMaxPlaceholders(pattern, 0, INT32_MAX, status);
535 + }
536 +
537 + /**
538 + * Compiles pattern and makes this object represent pattern.
539 + *
540 + * Returns TRUE on success; FALSE on failure. Will not fail if
541 + * there are three or fewer placeholders in pattern. May fail with
542 + * U_MEMORY_ALLOCATION_ERROR if there are more than three placeholders.
543 + *
544 + * @param min The pattern must have at least this many placeholders.
545 + * @param max The pattern must have at most this many placeholders.
546 + */
547 + UBool compileMinMaxPlaceholders(const UnicodeString &pattern,
548 + int32_t min, int32_t max, UErrorCode &statu s);
549
550 /**
551 * Returns (maxPlaceholderId + 1). For example
552 diff --git a/source/common/uresbund.cpp b/source/common/uresbund.cpp
553 index e74afb8..b2d071a 100644
554 --- a/source/common/uresbund.cpp
555 +++ b/source/common/uresbund.cpp
556 @@ -1882,6 +1882,121 @@ ures_getByKeyWithFallback(const UResourceBundle *resB,
557 return fillIn;
558 }
559
560 +namespace {
561 +
562 +void getAllContainerItemsWithFallback(
563 + const UResourceBundle *bundle, ResourceDataValue &value,
564 + ResourceArraySink *arraySink, ResourceTableSink *tableSink,
565 + UErrorCode &errorCode) {
566 + if (U_FAILURE(errorCode)) { return; }
567 + // We recursively enumerate child-first,
568 + // only storing parent items in the absence of child items.
569 + // We store a placeholder value for the no-fallback/no-inheritance marker
570 + // to prevent a parent item from being stored.
571 + //
572 + // It would be possible to recursively enumerate parent-first,
573 + // overriding parent items with child items.
574 + // When we see the no-fallback/no-inheritance marker,
575 + // then we would remove the parent's item.
576 + // We would deserialize parent values even though they are overridden in a child bundle.
577 + UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
578 + if (ures_getType(bundle) == expectedType) {
579 + value.pResData = &bundle->fResData;
580 + if (arraySink != NULL) {
581 + ures_getAllArrayItems(&bundle->fResData, bundle->fRes, value, *arra ySink, errorCode);
582 + } else /* tableSink != NULL */ {
583 + ures_getAllTableItems(&bundle->fResData, bundle->fRes, value, *tabl eSink, errorCode);
584 + }
585 + }
586 + UResourceDataEntry *entry = bundle->fData->fParent;
587 + if (entry != NULL && U_SUCCESS(entry->fBogus)) {
588 + // We might try to query the sink whether
589 + // any fallback from the parent bundle is still possible.
590 +
591 + // Turn the parent UResourceDataEntry into a UResourceBundle,
592 + // much like in ures_openWithType().
593 + // TODO: See if we can refactor ures_getByKeyWithFallback()
594 + // and pull out an inner function that takes and returns a UResourceDat aEntry
595 + // so that we need not create UResourceBundle objects.
596 + UResourceBundle parentBundle;
597 + ures_initStackObject(&parentBundle);
598 + parentBundle.fTopLevelData = parentBundle.fData = entry;
599 + // TODO: What is the difference between bundle fData and fTopLevelData?
600 + uprv_memcpy(&parentBundle.fResData, &entry->fData, sizeof(ResourceData) );
601 + // TODO: Try to replace bundle.fResData with just using bundle.fData->f Data.
602 + parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
603 + parentBundle.fIsTopLevel = TRUE;
604 + parentBundle.fRes = parentBundle.fResData.rootRes;
605 + parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), pare ntBundle.fRes);
606 + parentBundle.fIndex = -1;
607 + entryIncrease(entry);
608 +
609 + // Look up the container item in the parent bundle.
610 + UResourceBundle containerBundle;
611 + ures_initStackObject(&containerBundle);
612 + const UResourceBundle *rb;
613 + if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
614 + rb = &parentBundle;
615 + } else {
616 + rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
617 + &containerBundle, &errorCode);
618 + }
619 + if (U_SUCCESS(errorCode) && ures_getType(rb) == expectedType) {
620 + getAllContainerItemsWithFallback(rb, value,
621 + arraySink, tableSink, errorCode);
622 + }
623 + ures_close(&containerBundle);
624 + ures_close(&parentBundle);
625 + }
626 +}
627 +
628 +void getAllContainerItemsWithFallback(
629 + const UResourceBundle *bundle, const char *path,
630 + ResourceArraySink *arraySink, ResourceTableSink *tableSink,
631 + UErrorCode &errorCode) {
632 + if (U_FAILURE(errorCode)) { return; }
633 + if (path == NULL) {
634 + errorCode = U_ILLEGAL_ARGUMENT_ERROR;
635 + return;
636 + }
637 + UResourceBundle stackBundle;
638 + ures_initStackObject(&stackBundle);
639 + const UResourceBundle *rb;
640 + if (*path == 0) {
641 + // empty path
642 + rb = bundle;
643 + } else {
644 + rb = ures_getByKeyWithFallback(bundle, path, &stackBundle, &errorCode);
645 + if (U_FAILURE(errorCode)) {
646 + ures_close(&stackBundle);
647 + return;
648 + }
649 + }
650 + UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
651 + if (ures_getType(rb) != expectedType) {
652 + errorCode = U_RESOURCE_TYPE_MISMATCH;
653 + ures_close(&stackBundle);
654 + return;
655 + }
656 + // Get all table items with fallback.
657 + ResourceDataValue value;
658 + getAllContainerItemsWithFallback(rb, value, arraySink, tableSink, errorCode );
659 + ures_close(&stackBundle);
660 +}
661 +
662 +} // namespace
663 +
664 +U_CAPI void U_EXPORT2
665 +ures_getAllArrayItemsWithFallback(const UResourceBundle *bundle, const char *pa th,
666 + ResourceArraySink &sink, UErrorCode &errorCod e) {
667 + getAllContainerItemsWithFallback(bundle, path, &sink, NULL, errorCode);
668 +}
669 +
670 +U_CAPI void U_EXPORT2
671 +ures_getAllTableItemsWithFallback(const UResourceBundle *bundle, const char *pa th,
672 + ResourceTableSink &sink, UErrorCode &errorCod e) {
673 + getAllContainerItemsWithFallback(bundle, path, NULL, &sink, errorCode);
674 +}
675
676 U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, co nst char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
677 Resource res = RES_BOGUS;
678 diff --git a/source/common/uresdata.c b/source/common/uresdata.c
679 deleted file mode 100644
680 index 53887fb..0000000
681 --- a/source/common/uresdata.c
682 +++ /dev/null
683 @@ -1,1169 +0,0 @@
684 -/*
685 -*******************************************************************************
686 -* Copyright (C) 1999-2015, International Business Machines Corporation
687 -* and others. All Rights Reserved.
688 -*******************************************************************************
689 -* file name: uresdata.c
690 -* encoding: US-ASCII
691 -* tab size: 8 (not used)
692 -* indentation:4
693 -*
694 -* created on: 1999dec08
695 -* created by: Markus W. Scherer
696 -* Modification History:
697 -*
698 -* Date Name Description
699 -* 06/20/2000 helena OS/400 port changes; mostly typecast.
700 -* 06/24/02 weiv Added support for resource sharing
701 -*/
702 -
703 -#include "unicode/utypes.h"
704 -#include "unicode/udata.h"
705 -#include "unicode/ustring.h"
706 -#include "unicode/utf16.h"
707 -#include "cmemory.h"
708 -#include "cstring.h"
709 -#include "uarrsort.h"
710 -#include "udataswp.h"
711 -#include "ucol_swp.h"
712 -#include "uinvchar.h"
713 -#include "uresdata.h"
714 -#include "uresimp.h"
715 -#include "uassert.h"
716 -
717 -/*
718 - * Resource access helpers
719 - */
720 -
721 -/* get a const char* pointer to the key with the keyOffset byte offset from pRo ot */
722 -#define RES_GET_KEY16(pResData, keyOffset) \
723 - ((keyOffset)<(pResData)->localKeyLimit ? \
724 - (const char *)(pResData)->pRoot+(keyOffset) : \
725 - (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit)
726 -
727 -#define RES_GET_KEY32(pResData, keyOffset) \
728 - ((keyOffset)>=0 ? \
729 - (const char *)(pResData)->pRoot+(keyOffset) : \
730 - (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff))
731 -
732 -#define URESDATA_ITEM_NOT_FOUND -1
733 -
734 -/* empty resources, returned when the resource offset is 0 */
735 -static const uint16_t gEmpty16=0;
736 -
737 -static const struct {
738 - int32_t length;
739 - int32_t res;
740 -} gEmpty32={ 0, 0 };
741 -
742 -static const struct {
743 - int32_t length;
744 - UChar nul;
745 - UChar pad;
746 -} gEmptyString={ 0, 0, 0 };
747 -
748 -/*
749 - * All the type-access functions assume that
750 - * the resource is of the expected type.
751 - */
752 -
753 -static int32_t
754 -_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, in t32_t length,
755 - const char *key, const char **realKey) {
756 - const char *tableKey;
757 - int32_t mid, start, limit;
758 - int result;
759 -
760 - /* do a binary search for the key */
761 - start=0;
762 - limit=length;
763 - while(start<limit) {
764 - mid = (start + limit) / 2;
765 - tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
766 - if (pResData->useNativeStrcmp) {
767 - result = uprv_strcmp(key, tableKey);
768 - } else {
769 - result = uprv_compareInvCharsAsAscii(key, tableKey);
770 - }
771 - if (result < 0) {
772 - limit = mid;
773 - } else if (result > 0) {
774 - start = mid + 1;
775 - } else {
776 - /* We found it! */
777 - *realKey=tableKey;
778 - return mid;
779 - }
780 - }
781 - return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */
782 -}
783 -
784 -static int32_t
785 -_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, i nt32_t length,
786 - const char *key, const char **realKey) {
787 - const char *tableKey;
788 - int32_t mid, start, limit;
789 - int result;
790 -
791 - /* do a binary search for the key */
792 - start=0;
793 - limit=length;
794 - while(start<limit) {
795 - mid = (start + limit) / 2;
796 - tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
797 - if (pResData->useNativeStrcmp) {
798 - result = uprv_strcmp(key, tableKey);
799 - } else {
800 - result = uprv_compareInvCharsAsAscii(key, tableKey);
801 - }
802 - if (result < 0) {
803 - limit = mid;
804 - } else if (result > 0) {
805 - start = mid + 1;
806 - } else {
807 - /* We found it! */
808 - *realKey=tableKey;
809 - return mid;
810 - }
811 - }
812 - return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */
813 -}
814 -
815 -/* helper for res_load() ---------------------------------------------------- * /
816 -
817 -static UBool U_CALLCONV
818 -isAcceptable(void *context,
819 - const char *type, const char *name,
820 - const UDataInfo *pInfo) {
821 - uprv_memcpy(context, pInfo->formatVersion, 4);
822 - return (UBool)(
823 - pInfo->size>=20 &&
824 - pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
825 - pInfo->charsetFamily==U_CHARSET_FAMILY &&
826 - pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
827 - pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
828 - pInfo->dataFormat[1]==0x65 &&
829 - pInfo->dataFormat[2]==0x73 &&
830 - pInfo->dataFormat[3]==0x42 &&
831 - (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3));
832 -}
833 -
834 -/* semi-public functions ---------------------------------------------------- * /
835 -
836 -static void
837 -res_init(ResourceData *pResData,
838 - UVersionInfo formatVersion, const void *inBytes, int32_t length,
839 - UErrorCode *errorCode) {
840 - UResType rootType;
841 -
842 - /* get the root resource */
843 - pResData->pRoot=(const int32_t *)inBytes;
844 - pResData->rootRes=(Resource)*pResData->pRoot;
845 - pResData->p16BitUnits=&gEmpty16;
846 -
847 - /* formatVersion 1.1 must have a root item and at least 5 indexes */
848 - if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
849 - *errorCode=U_INVALID_FORMAT_ERROR;
850 - res_unload(pResData);
851 - return;
852 - }
853 -
854 - /* currently, we accept only resources that have a Table as their roots */
855 - rootType=(UResType)RES_GET_TYPE(pResData->rootRes);
856 - if(!URES_IS_TABLE(rootType)) {
857 - *errorCode=U_INVALID_FORMAT_ERROR;
858 - res_unload(pResData);
859 - return;
860 - }
861 -
862 - if(formatVersion[0]==1 && formatVersion[1]==0) {
863 - pResData->localKeyLimit=0x10000; /* greater than any 16-bit key string offset */
864 - } else {
865 - /* bundles with formatVersion 1.1 and later contain an indexes[] array */
866 - const int32_t *indexes=pResData->pRoot+1;
867 - int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
868 - if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
869 - *errorCode=U_INVALID_FORMAT_ERROR;
870 - res_unload(pResData);
871 - return;
872 - }
873 - if( length>=0 &&
874 - (length<((1+indexLength)<<2) ||
875 - length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
876 - ) {
877 - *errorCode=U_INVALID_FORMAT_ERROR;
878 - res_unload(pResData);
879 - return;
880 - }
881 - if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
882 - pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
883 - }
884 - if(formatVersion[0]>=3) {
885 - // In formatVersion 1, the indexLength took up this whole int.
886 - // In version 2, bits 31..8 were reserved and always 0.
887 - // In version 3, they contain bits 23..0 of the poolStringIndexLimi t.
888 - // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12.
889 - pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_IND EX_LENGTH]>>8);
890 - }
891 - if(indexLength>URES_INDEX_ATTRIBUTES) {
892 - int32_t att=indexes[URES_INDEX_ATTRIBUTES];
893 - pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK);
894 - pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0);
895 - pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0 );
896 - pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 - > 27..24
897 - pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16);
898 - }
899 - if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength< =URES_INDEX_POOL_CHECKSUM) {
900 - *errorCode=U_INVALID_FORMAT_ERROR;
901 - res_unload(pResData);
902 - return;
903 - }
904 - if( indexLength>URES_INDEX_16BIT_TOP &&
905 - indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
906 - ) {
907 - pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[UR ES_INDEX_KEYS_TOP]);
908 - }
909 - }
910 -
911 - if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) {
912 - /*
913 - * formatVersion 1: compare key strings in native-charset order
914 - * formatVersion 2 and up: compare key strings in ASCII order
915 - */
916 - pResData->useNativeStrcmp=TRUE;
917 - }
918 -}
919 -
920 -U_CAPI void U_EXPORT2
921 -res_read(ResourceData *pResData,
922 - const UDataInfo *pInfo, const void *inBytes, int32_t length,
923 - UErrorCode *errorCode) {
924 - UVersionInfo formatVersion;
925 -
926 - uprv_memset(pResData, 0, sizeof(ResourceData));
927 - if(U_FAILURE(*errorCode)) {
928 - return;
929 - }
930 - if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) {
931 - *errorCode=U_INVALID_FORMAT_ERROR;
932 - return;
933 - }
934 - res_init(pResData, formatVersion, inBytes, length, errorCode);
935 -}
936 -
937 -U_CFUNC void
938 -res_load(ResourceData *pResData,
939 - const char *path, const char *name, UErrorCode *errorCode) {
940 - UVersionInfo formatVersion;
941 -
942 - uprv_memset(pResData, 0, sizeof(ResourceData));
943 -
944 - /* load the ResourceBundle file */
945 - pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVers ion, errorCode);
946 - if(U_FAILURE(*errorCode)) {
947 - return;
948 - }
949 -
950 - /* get its memory and initialize *pResData */
951 - res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, erro rCode);
952 -}
953 -
954 -U_CFUNC void
955 -res_unload(ResourceData *pResData) {
956 - if(pResData->data!=NULL) {
957 - udata_close(pResData->data);
958 - pResData->data=NULL;
959 - }
960 -}
961 -
962 -static const int8_t gPublicTypes[URES_LIMIT] = {
963 - URES_STRING,
964 - URES_BINARY,
965 - URES_TABLE,
966 - URES_ALIAS,
967 -
968 - URES_TABLE, /* URES_TABLE32 */
969 - URES_TABLE, /* URES_TABLE16 */
970 - URES_STRING, /* URES_STRING_V2 */
971 - URES_INT,
972 -
973 - URES_ARRAY,
974 - URES_ARRAY, /* URES_ARRAY16 */
975 - URES_NONE,
976 - URES_NONE,
977 -
978 - URES_NONE,
979 - URES_NONE,
980 - URES_INT_VECTOR,
981 - URES_NONE
982 -};
983 -
984 -U_CAPI UResType U_EXPORT2
985 -res_getPublicType(Resource res) {
986 - return (UResType)gPublicTypes[RES_GET_TYPE(res)];
987 -}
988 -
989 -U_CAPI const UChar * U_EXPORT2
990 -res_getString(const ResourceData *pResData, Resource res, int32_t *pLength) {
991 - const UChar *p;
992 - uint32_t offset=RES_GET_OFFSET(res);
993 - int32_t length;
994 - if(RES_GET_TYPE(res)==URES_STRING_V2) {
995 - int32_t first;
996 - if(offset<pResData->poolStringIndexLimit) {
997 - p=(const UChar *)pResData->poolBundleStrings+offset;
998 - } else {
999 - p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolString IndexLimit);
1000 - }
1001 - first=*p;
1002 - if(!U16_IS_TRAIL(first)) {
1003 - length=u_strlen(p);
1004 - } else if(first<0xdfef) {
1005 - length=first&0x3ff;
1006 - ++p;
1007 - } else if(first<0xdfff) {
1008 - length=((first-0xdfef)<<16)|p[1];
1009 - p+=2;
1010 - } else {
1011 - length=((int32_t)p[1]<<16)|p[2];
1012 - p+=3;
1013 - }
1014 - } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
1015 - const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res ;
1016 - length=*p32++;
1017 - p=(const UChar *)p32;
1018 - } else {
1019 - p=NULL;
1020 - length=0;
1021 - }
1022 - if(pLength) {
1023 - *pLength=length;
1024 - }
1025 - return p;
1026 -}
1027 -
1028 -U_CAPI const UChar * U_EXPORT2
1029 -res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
1030 - const UChar *p;
1031 - uint32_t offset=RES_GET_OFFSET(res);
1032 - int32_t length;
1033 - if(RES_GET_TYPE(res)==URES_ALIAS) {
1034 - const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+ offset;
1035 - length=*p32++;
1036 - p=(const UChar *)p32;
1037 - } else {
1038 - p=NULL;
1039 - length=0;
1040 - }
1041 - if(pLength) {
1042 - *pLength=length;
1043 - }
1044 - return p;
1045 -}
1046 -
1047 -U_CAPI const uint8_t * U_EXPORT2
1048 -res_getBinary(const ResourceData *pResData, Resource res, int32_t *pLength) {
1049 - const uint8_t *p;
1050 - uint32_t offset=RES_GET_OFFSET(res);
1051 - int32_t length;
1052 - if(RES_GET_TYPE(res)==URES_BINARY) {
1053 - const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->p Root+offset;
1054 - length=*p32++;
1055 - p=(const uint8_t *)p32;
1056 - } else {
1057 - p=NULL;
1058 - length=0;
1059 - }
1060 - if(pLength) {
1061 - *pLength=length;
1062 - }
1063 - return p;
1064 -}
1065 -
1066 -
1067 -U_CAPI const int32_t * U_EXPORT2
1068 -res_getIntVector(const ResourceData *pResData, Resource res, int32_t *pLength) {
1069 - const int32_t *p;
1070 - uint32_t offset=RES_GET_OFFSET(res);
1071 - int32_t length;
1072 - if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
1073 - p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset;
1074 - length=*p++;
1075 - } else {
1076 - p=NULL;
1077 - length=0;
1078 - }
1079 - if(pLength) {
1080 - *pLength=length;
1081 - }
1082 - return p;
1083 -}
1084 -
1085 -U_CAPI int32_t U_EXPORT2
1086 -res_countArrayItems(const ResourceData *pResData, Resource res) {
1087 - uint32_t offset=RES_GET_OFFSET(res);
1088 - switch(RES_GET_TYPE(res)) {
1089 - case URES_STRING:
1090 - case URES_STRING_V2:
1091 - case URES_BINARY:
1092 - case URES_ALIAS:
1093 - case URES_INT:
1094 - case URES_INT_VECTOR:
1095 - return 1;
1096 - case URES_ARRAY:
1097 - case URES_TABLE32:
1098 - return offset==0 ? 0 : *(pResData->pRoot+offset);
1099 - case URES_TABLE:
1100 - return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
1101 - case URES_ARRAY16:
1102 - case URES_TABLE16:
1103 - return pResData->p16BitUnits[offset];
1104 - default:
1105 - return 0;
1106 - }
1107 -}
1108 -
1109 -static Resource
1110 -makeResourceFrom16(const ResourceData *pResData, int32_t res16) {
1111 - if(res16<pResData->poolStringIndex16Limit) {
1112 - // Pool string, nothing to do.
1113 - } else {
1114 - // Local string, adjust the 16-bit offset to a regular one,
1115 - // with a larger pool string index limit.
1116 - res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexL imit;
1117 - }
1118 - return URES_MAKE_RESOURCE(URES_STRING_V2, res16);
1119 -}
1120 -
1121 -U_CAPI Resource U_EXPORT2
1122 -res_getTableItemByKey(const ResourceData *pResData, Resource table,
1123 - int32_t *indexR, const char **key) {
1124 - uint32_t offset=RES_GET_OFFSET(table);
1125 - int32_t length;
1126 - int32_t idx;
1127 - if(key == NULL || *key == NULL) {
1128 - return RES_BOGUS;
1129 - }
1130 - switch(RES_GET_TYPE(table)) {
1131 - case URES_TABLE: {
1132 - if (offset!=0) { /* empty if offset==0 */
1133 - const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
1134 - length=*p++;
1135 - *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
1136 - if(idx>=0) {
1137 - const Resource *p32=(const Resource *)(p+length+(~length&1));
1138 - return p32[idx];
1139 - }
1140 - }
1141 - break;
1142 - }
1143 - case URES_TABLE16: {
1144 - const uint16_t *p=pResData->p16BitUnits+offset;
1145 - length=*p++;
1146 - *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
1147 - if(idx>=0) {
1148 - return makeResourceFrom16(pResData, p[length+idx]);
1149 - }
1150 - break;
1151 - }
1152 - case URES_TABLE32: {
1153 - if (offset!=0) { /* empty if offset==0 */
1154 - const int32_t *p= pResData->pRoot+offset;
1155 - length=*p++;
1156 - *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
1157 - if(idx>=0) {
1158 - return (Resource)p[length+idx];
1159 - }
1160 - }
1161 - break;
1162 - }
1163 - default:
1164 - break;
1165 - }
1166 - return RES_BOGUS;
1167 -}
1168 -
1169 -U_CAPI Resource U_EXPORT2
1170 -res_getTableItemByIndex(const ResourceData *pResData, Resource table,
1171 - int32_t indexR, const char **key) {
1172 - uint32_t offset=RES_GET_OFFSET(table);
1173 - int32_t length;
1174 - U_ASSERT(indexR>=0); /* to ensure the index is not negative */
1175 - switch(RES_GET_TYPE(table)) {
1176 - case URES_TABLE: {
1177 - if (offset != 0) { /* empty if offset==0 */
1178 - const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
1179 - length=*p++;
1180 - if(indexR<length) {
1181 - const Resource *p32=(const Resource *)(p+length+(~length&1));
1182 - if(key!=NULL) {
1183 - *key=RES_GET_KEY16(pResData, p[indexR]);
1184 - }
1185 - return p32[indexR];
1186 - }
1187 - }
1188 - break;
1189 - }
1190 - case URES_TABLE16: {
1191 - const uint16_t *p=pResData->p16BitUnits+offset;
1192 - length=*p++;
1193 - if(indexR<length) {
1194 - if(key!=NULL) {
1195 - *key=RES_GET_KEY16(pResData, p[indexR]);
1196 - }
1197 - return makeResourceFrom16(pResData, p[length+indexR]);
1198 - }
1199 - break;
1200 - }
1201 - case URES_TABLE32: {
1202 - if (offset != 0) { /* empty if offset==0 */
1203 - const int32_t *p= pResData->pRoot+offset;
1204 - length=*p++;
1205 - if(indexR<length) {
1206 - if(key!=NULL) {
1207 - *key=RES_GET_KEY32(pResData, p[indexR]);
1208 - }
1209 - return (Resource)p[length+indexR];
1210 - }
1211 - }
1212 - break;
1213 - }
1214 - default:
1215 - break;
1216 - }
1217 - return RES_BOGUS;
1218 -}
1219 -
1220 -U_CAPI Resource U_EXPORT2
1221 -res_getResource(const ResourceData *pResData, const char *key) {
1222 - const char *realKey=key;
1223 - int32_t idx;
1224 - return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
1225 -}
1226 -
1227 -U_CAPI Resource U_EXPORT2
1228 -res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
1229 - uint32_t offset=RES_GET_OFFSET(array);
1230 - U_ASSERT(indexR>=0); /* to ensure the index is not negative */
1231 - switch(RES_GET_TYPE(array)) {
1232 - case URES_ARRAY: {
1233 - if (offset!=0) { /* empty if offset==0 */
1234 - const int32_t *p= pResData->pRoot+offset;
1235 - if(indexR<*p) {
1236 - return (Resource)p[1+indexR];
1237 - }
1238 - }
1239 - break;
1240 - }
1241 - case URES_ARRAY16: {
1242 - const uint16_t *p=pResData->p16BitUnits+offset;
1243 - if(indexR<*p) {
1244 - return makeResourceFrom16(pResData, p[1+indexR]);
1245 - }
1246 - break;
1247 - }
1248 - default:
1249 - break;
1250 - }
1251 - return RES_BOGUS;
1252 -}
1253 -
1254 -U_CFUNC Resource
1255 -res_findResource(const ResourceData *pResData, Resource r, char** path, const c har** key) {
1256 - /* we pass in a path. CollationElements/Sequence or zoneStrings/3/2 etc.
1257 - * iterates over a path and stops when a scalar resource is found. This
1258 - * CAN be an alias. Path gets set to the part that has not yet been processed .
1259 - */
1260 -
1261 - char *pathP = *path, *nextSepP = *path;
1262 - char *closeIndex = NULL;
1263 - Resource t1 = r;
1264 - Resource t2;
1265 - int32_t indexR = 0;
1266 - UResType type = (UResType)RES_GET_TYPE(t1);
1267 -
1268 - /* if you come in with an empty path, you'll be getting back the same resourc e */
1269 - if(!uprv_strlen(pathP)) {
1270 - return r;
1271 - }
1272 -
1273 - /* one needs to have an aggregate resource in order to search in it */
1274 - if(!URES_IS_CONTAINER(type)) {
1275 - return RES_BOGUS;
1276 - }
1277 -
1278 - while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) {
1279 - /* Iteration stops if: the path has been consumed, we found a non-existing
1280 - * resource (t1 == RES_BOGUS) or we found a scalar resource (including alia s)
1281 - */
1282 - nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR);
1283 - /* if there are more separators, terminate string
1284 - * and set path to the remaining part of the string
1285 - */
1286 - if(nextSepP != NULL) {
1287 - *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
1288 - *path = nextSepP+1;
1289 - } else {
1290 - *path = uprv_strchr(pathP, 0);
1291 - }
1292 -
1293 - /* if the resource is a table */
1294 - /* try the key based access */
1295 - if(URES_IS_TABLE(type)) {
1296 - *key = pathP;
1297 - t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
1298 - if(t2 == RES_BOGUS) {
1299 - /* if we fail to get the resource by key, maybe we got an index */
1300 - indexR = uprv_strtol(pathP, &closeIndex, 10);
1301 - if(closeIndex != pathP) {
1302 - /* if we indeed have an index, try to get the item by index */
1303 - t2 = res_getTableItemByIndex(pResData, t1, indexR, key);
1304 - }
1305 - }
1306 - } else if(URES_IS_ARRAY(type)) {
1307 - indexR = uprv_strtol(pathP, &closeIndex, 10);
1308 - if(closeIndex != pathP) {
1309 - t2 = res_getArrayItem(pResData, t1, indexR);
1310 - } else {
1311 - t2 = RES_BOGUS; /* have an array, but don't have a valid index */
1312 - }
1313 - *key = NULL;
1314 - } else { /* can't do much here, except setting t2 to bogus */
1315 - t2 = RES_BOGUS;
1316 - }
1317 - t1 = t2;
1318 - type = (UResType)RES_GET_TYPE(t1);
1319 - /* position pathP to next resource key/index */
1320 - pathP = *path;
1321 - }
1322 -
1323 - return t1;
1324 -}
1325 -
1326 -/* resource bundle swapping ------------------------------------------------- * /
1327 -
1328 -/*
1329 - * Need to always enumerate the entire item tree,
1330 - * track the lowest address of any item to use as the limit for char keys[],
1331 - * track the highest address of any item to return the size of the data.
1332 - *
1333 - * We should have thought of storing those in the data...
1334 - * It is possible to extend the data structure by putting additional values
1335 - * in places that are inaccessible by ordinary enumeration of the item tree.
1336 - * For example, additional integers could be stored at the beginning or
1337 - * end of the key strings; this could be indicated by a minor version number,
1338 - * and the data swapping would have to know about these values.
1339 - *
1340 - * The data structure does not forbid keys to be shared, so we must swap
1341 - * all keys once instead of each key when it is referenced.
1342 - *
1343 - * These swapping functions assume that a resource bundle always has a length
1344 - * that is a multiple of 4 bytes.
1345 - * Currently, this is trivially true because genrb writes bundle tree leaves
1346 - * physically first, before their branches, so that the root table with its
1347 - * array of resource items (uint32_t values) is always last.
1348 - */
1349 -
1350 -/* definitions for table sorting ------------------------ */
1351 -
1352 -/*
1353 - * row of a temporary array
1354 - *
1355 - * gets platform-endian key string indexes and sorting indexes;
1356 - * after sorting this array by keys, the actual key/value arrays are permutated
1357 - * according to the sorting indexes
1358 - */
1359 -typedef struct Row {
1360 - int32_t keyIndex, sortIndex;
1361 -} Row;
1362 -
1363 -static int32_t
1364 -ures_compareRows(const void *context, const void *left, const void *right) {
1365 - const char *keyChars=(const char *)context;
1366 - return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex,
1367 - keyChars+((const Row *)right)->keyIndex);
1368 -}
1369 -
1370 -typedef struct TempTable {
1371 - const char *keyChars;
1372 - Row *rows;
1373 - int32_t *resort;
1374 - uint32_t *resFlags;
1375 - int32_t localKeyLimit;
1376 - uint8_t majorFormatVersion;
1377 -} TempTable;
1378 -
1379 -enum {
1380 - STACK_ROW_CAPACITY=200
1381 -};
1382 -
1383 -/* The table item key string is not locally available. */
1384 -static const char *const gUnknownKey="";
1385 -
1386 -/* resource table key for collation binaries: "%%CollationBin" */
1387 -static const UChar gCollationBinKey[]={
1388 - 0x25, 0x25,
1389 - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
1390 - 0x42, 0x69, 0x6e,
1391 - 0
1392 -};
1393 -
1394 -/*
1395 - * swap one resource item
1396 - */
1397 -static void
1398 -ures_swapResource(const UDataSwapper *ds,
1399 - const Resource *inBundle, Resource *outBundle,
1400 - Resource res, /* caller swaps res itself */
1401 - const char *key,
1402 - TempTable *pTempTable,
1403 - UErrorCode *pErrorCode) {
1404 - const Resource *p;
1405 - Resource *q;
1406 - int32_t offset, count;
1407 -
1408 - switch(RES_GET_TYPE(res)) {
1409 - case URES_TABLE16:
1410 - case URES_STRING_V2:
1411 - case URES_INT:
1412 - case URES_ARRAY16:
1413 - /* integer, or points to 16-bit units, nothing to do here */
1414 - return;
1415 - default:
1416 - break;
1417 - }
1418 -
1419 - /* all other types use an offset to point to their data */
1420 - offset=(int32_t)RES_GET_OFFSET(res);
1421 - if(offset==0) {
1422 - /* special offset indicating an empty item */
1423 - return;
1424 - }
1425 - if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) {
1426 - /* we already swapped this resource item */
1427 - return;
1428 - } else {
1429 - /* mark it as swapped now */
1430 - pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f));
1431 - }
1432 -
1433 - p=inBundle+offset;
1434 - q=outBundle+offset;
1435 -
1436 - switch(RES_GET_TYPE(res)) {
1437 - case URES_ALIAS:
1438 - /* physically same value layout as string, fall through */
1439 - case URES_STRING:
1440 - count=udata_readInt32(ds, (int32_t)*p);
1441 - /* swap length */
1442 - ds->swapArray32(ds, p, 4, q, pErrorCode);
1443 - /* swap each UChar (the terminating NUL would not change) */
1444 - ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
1445 - break;
1446 - case URES_BINARY:
1447 - count=udata_readInt32(ds, (int32_t)*p);
1448 - /* swap length */
1449 - ds->swapArray32(ds, p, 4, q, pErrorCode);
1450 - /* no need to swap or copy bytes - ures_swap() copied them all */
1451 -
1452 - /* swap known formats */
1453 -#if !UCONFIG_NO_COLLATION
1454 - if( key!=NULL && /* the binary is in a table */
1455 - (key!=gUnknownKey ?
1456 - /* its table key string is "%%CollationBin" */
1457 - 0==ds->compareInvChars(ds, key, -1,
1458 - gCollationBinKey, UPRV_LENGTHOF(gCollati onBinKey)-1) :
1459 - /* its table key string is unknown but it looks like a collatio n binary */
1460 - ucol_looksLikeCollationBinary(ds, p+1, count))
1461 - ) {
1462 - ucol_swap(ds, p+1, count, q+1, pErrorCode);
1463 - }
1464 -#endif
1465 - break;
1466 - case URES_TABLE:
1467 - case URES_TABLE32:
1468 - {
1469 - const uint16_t *pKey16;
1470 - uint16_t *qKey16;
1471 -
1472 - const int32_t *pKey32;
1473 - int32_t *qKey32;
1474 -
1475 - Resource item;
1476 - int32_t i, oldIndex;
1477 -
1478 - if(RES_GET_TYPE(res)==URES_TABLE) {
1479 - /* get table item count */
1480 - pKey16=(const uint16_t *)p;
1481 - qKey16=(uint16_t *)q;
1482 - count=ds->readUInt16(*pKey16);
1483 -
1484 - pKey32=qKey32=NULL;
1485 -
1486 - /* swap count */
1487 - ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
1488 -
1489 - offset+=((1+count)+1)/2;
1490 - } else {
1491 - /* get table item count */
1492 - pKey32=(const int32_t *)p;
1493 - qKey32=(int32_t *)q;
1494 - count=udata_readInt32(ds, *pKey32);
1495 -
1496 - pKey16=qKey16=NULL;
1497 -
1498 - /* swap count */
1499 - ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
1500 -
1501 - offset+=1+count;
1502 - }
1503 -
1504 - if(count==0) {
1505 - break;
1506 - }
1507 -
1508 - p=inBundle+offset; /* pointer to table resources */
1509 - q=outBundle+offset;
1510 -
1511 - /* recurse */
1512 - for(i=0; i<count; ++i) {
1513 - const char *itemKey=gUnknownKey;
1514 - if(pKey16!=NULL) {
1515 - int32_t keyOffset=ds->readUInt16(pKey16[i]);
1516 - if(keyOffset<pTempTable->localKeyLimit) {
1517 - itemKey=(const char *)outBundle+keyOffset;
1518 - }
1519 - } else {
1520 - int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
1521 - if(keyOffset>=0) {
1522 - itemKey=(const char *)outBundle+keyOffset;
1523 - }
1524 - }
1525 - item=ds->readUInt32(p[i]);
1526 - ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTemp Table, pErrorCode);
1527 - if(U_FAILURE(*pErrorCode)) {
1528 - udata_printError(ds, "ures_swapResource(table res=%08x)[%d] .recurse(%08x) failed\n",
1529 - res, i, item);
1530 - return;
1531 - }
1532 - }
1533 -
1534 - if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharse t) {
1535 - /* no need to sort, just swap the offset/value arrays */
1536 - if(pKey16!=NULL) {
1537 - ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
1538 - ds->swapArray32(ds, p, count*4, q, pErrorCode);
1539 - } else {
1540 - /* swap key offsets and items as one array */
1541 - ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
1542 - }
1543 - break;
1544 - }
1545 -
1546 - /*
1547 - * We need to sort tables by outCharset key strings because they
1548 - * sort differently for different charset families.
1549 - * ures_swap() already set pTempTable->keyChars appropriately.
1550 - * First we set up a temporary table with the key indexes and
1551 - * sorting indexes and sort that.
1552 - * Then we permutate and copy/swap the actual values.
1553 - */
1554 - if(pKey16!=NULL) {
1555 - for(i=0; i<count; ++i) {
1556 - pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
1557 - pTempTable->rows[i].sortIndex=i;
1558 - }
1559 - } else {
1560 - for(i=0; i<count; ++i) {
1561 - pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]) ;
1562 - pTempTable->rows[i].sortIndex=i;
1563 - }
1564 - }
1565 - uprv_sortArray(pTempTable->rows, count, sizeof(Row),
1566 - ures_compareRows, pTempTable->keyChars,
1567 - FALSE, pErrorCode);
1568 - if(U_FAILURE(*pErrorCode)) {
1569 - udata_printError(ds, "ures_swapResource(table res=%08x).uprv_so rtArray(%d items) failed\n",
1570 - res, count);
1571 - return;
1572 - }
1573 -
1574 - /*
1575 - * copy/swap/permutate items
1576 - *
1577 - * If we swap in-place, then the permutation must use another
1578 - * temporary array (pTempTable->resort)
1579 - * before the results are copied to the outBundle.
1580 - */
1581 - /* keys */
1582 - if(pKey16!=NULL) {
1583 - uint16_t *rKey16;
1584 -
1585 - if(pKey16!=qKey16) {
1586 - rKey16=qKey16;
1587 - } else {
1588 - rKey16=(uint16_t *)pTempTable->resort;
1589 - }
1590 - for(i=0; i<count; ++i) {
1591 - oldIndex=pTempTable->rows[i].sortIndex;
1592 - ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCod e);
1593 - }
1594 - if(qKey16!=rKey16) {
1595 - uprv_memcpy(qKey16, rKey16, 2*count);
1596 - }
1597 - } else {
1598 - int32_t *rKey32;
1599 -
1600 - if(pKey32!=qKey32) {
1601 - rKey32=qKey32;
1602 - } else {
1603 - rKey32=pTempTable->resort;
1604 - }
1605 - for(i=0; i<count; ++i) {
1606 - oldIndex=pTempTable->rows[i].sortIndex;
1607 - ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCod e);
1608 - }
1609 - if(qKey32!=rKey32) {
1610 - uprv_memcpy(qKey32, rKey32, 4*count);
1611 - }
1612 - }
1613 -
1614 - /* resources */
1615 - {
1616 - Resource *r;
1617 -
1618 -
1619 - if(p!=q) {
1620 - r=q;
1621 - } else {
1622 - r=(Resource *)pTempTable->resort;
1623 - }
1624 - for(i=0; i<count; ++i) {
1625 - oldIndex=pTempTable->rows[i].sortIndex;
1626 - ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
1627 - }
1628 - if(q!=r) {
1629 - uprv_memcpy(q, r, 4*count);
1630 - }
1631 - }
1632 - }
1633 - break;
1634 - case URES_ARRAY:
1635 - {
1636 - Resource item;
1637 - int32_t i;
1638 -
1639 - count=udata_readInt32(ds, (int32_t)*p);
1640 - /* swap length */
1641 - ds->swapArray32(ds, p++, 4, q++, pErrorCode);
1642 -
1643 - /* recurse */
1644 - for(i=0; i<count; ++i) {
1645 - item=ds->readUInt32(p[i]);
1646 - ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTab le, pErrorCode);
1647 - if(U_FAILURE(*pErrorCode)) {
1648 - udata_printError(ds, "ures_swapResource(array res=%08x)[%d] .recurse(%08x) failed\n",
1649 - res, i, item);
1650 - return;
1651 - }
1652 - }
1653 -
1654 - /* swap items */
1655 - ds->swapArray32(ds, p, 4*count, q, pErrorCode);
1656 - }
1657 - break;
1658 - case URES_INT_VECTOR:
1659 - count=udata_readInt32(ds, (int32_t)*p);
1660 - /* swap length and each integer */
1661 - ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
1662 - break;
1663 - default:
1664 - /* also catches RES_BOGUS */
1665 - *pErrorCode=U_UNSUPPORTED_ERROR;
1666 - break;
1667 - }
1668 -}
1669 -
1670 -U_CAPI int32_t U_EXPORT2
1671 -ures_swap(const UDataSwapper *ds,
1672 - const void *inData, int32_t length, void *outData,
1673 - UErrorCode *pErrorCode) {
1674 - const UDataInfo *pInfo;
1675 - const Resource *inBundle;
1676 - Resource rootRes;
1677 - int32_t headerSize, maxTableLength;
1678 -
1679 - Row rows[STACK_ROW_CAPACITY];
1680 - int32_t resort[STACK_ROW_CAPACITY];
1681 - TempTable tempTable;
1682 -
1683 - const int32_t *inIndexes;
1684 -
1685 - /* the following integers count Resource item offsets (4 bytes each), not b ytes */
1686 - int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top;
1687 -
1688 - /* udata_swapDataHeader checks the arguments */
1689 - headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
1690 - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
1691 - return 0;
1692 - }
1693 -
1694 - /* check data format and format version */
1695 - pInfo=(const UDataInfo *)((const char *)inData+4);
1696 - if(!(
1697 - pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
1698 - pInfo->dataFormat[1]==0x65 &&
1699 - pInfo->dataFormat[2]==0x73 &&
1700 - pInfo->dataFormat[3]==0x42 &&
1701 - /* formatVersion 1.1+ or 2.x or 3.x */
1702 - ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||
1703 - pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3)
1704 - )) {
1705 - udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (for mat version %02x.%02x) is not a resource bundle\n",
1706 - pInfo->dataFormat[0], pInfo->dataFormat[1],
1707 - pInfo->dataFormat[2], pInfo->dataFormat[3],
1708 - pInfo->formatVersion[0], pInfo->formatVersion[1]);
1709 - *pErrorCode=U_UNSUPPORTED_ERROR;
1710 - return 0;
1711 - }
1712 - tempTable.majorFormatVersion=pInfo->formatVersion[0];
1713 -
1714 - /* a resource bundle must contain at least one resource item */
1715 - if(length<0) {
1716 - bundleLength=-1;
1717 - } else {
1718 - bundleLength=(length-headerSize)/4;
1719 -
1720 - /* formatVersion 1.1 must have a root item and at least 5 indexes */
1721 - if(bundleLength<(1+5)) {
1722 - udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
1723 - length-headerSize);
1724 - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1725 - return 0;
1726 - }
1727 - }
1728 -
1729 - inBundle=(const Resource *)((const char *)inData+headerSize);
1730 - rootRes=ds->readUInt32(*inBundle);
1731 -
1732 - /* formatVersion 1.1 adds the indexes[] array */
1733 - inIndexes=(const int32_t *)(inBundle+1);
1734 -
1735 - indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
1736 - if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
1737 - udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
1738 - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1739 - return 0;
1740 - }
1741 - keysBottom=1+indexLength;
1742 - keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
1743 - if(indexLength>URES_INDEX_16BIT_TOP) {
1744 - resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
1745 - } else {
1746 - resBottom=keysTop;
1747 - }
1748 - top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
1749 - maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
1750 -
1751 - if(0<=bundleLength && bundleLength<top) {
1752 - udata_printError(ds, "ures_swap(): resource top %d exceeds bundle lengt h %d\n",
1753 - top, bundleLength);
1754 - *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1755 - return 0;
1756 - }
1757 - if(keysTop>(1+indexLength)) {
1758 - tempTable.localKeyLimit=keysTop<<2;
1759 - } else {
1760 - tempTable.localKeyLimit=0;
1761 - }
1762 -
1763 - if(length>=0) {
1764 - Resource *outBundle=(Resource *)((char *)outData+headerSize);
1765 -
1766 - /* track which resources we have already swapped */
1767 - uint32_t stackResFlags[STACK_ROW_CAPACITY];
1768 - int32_t resFlagsLength;
1769 -
1770 - /*
1771 - * We need one bit per 4 resource bundle bytes so that we can track
1772 - * every possible Resource for whether we have swapped it already.
1773 - * Multiple Resource words can refer to the same bundle offsets
1774 - * for sharing identical values.
1775 - * We could optimize this by allocating only for locations above
1776 - * where Resource values are stored (above keys & strings).
1777 - */
1778 - resFlagsLength=(length+31)>>5; /* number of bytes needed */
1779 - resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint 32_t */
1780 - if(resFlagsLength<=sizeof(stackResFlags)) {
1781 - tempTable.resFlags=stackResFlags;
1782 - } else {
1783 - tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
1784 - if(tempTable.resFlags==NULL) {
1785 - udata_printError(ds, "ures_swap(): unable to allocate memory fo r tracking resources\n");
1786 - *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1787 - return 0;
1788 - }
1789 - }
1790 - uprv_memset(tempTable.resFlags, 0, resFlagsLength);
1791 -
1792 - /* copy the bundle for binary and inaccessible data */
1793 - if(inData!=outData) {
1794 - uprv_memcpy(outBundle, inBundle, 4*top);
1795 - }
1796 -
1797 - /* swap the key strings, but not the padding bytes (0xaa) after the las t string and its NUL */
1798 - udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom ),
1799 - outBundle+keysBottom, pErrorCode);
1800 - if(U_FAILURE(*pErrorCode)) {
1801 - udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d] ) failed\n", 4*(keysTop-keysBottom));
1802 - return 0;
1803 - }
1804 -
1805 - /* swap the 16-bit units (strings, table16, array16) */
1806 - if(keysTop<resBottom) {
1807 - ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBun dle+keysTop, pErrorCode);
1808 - if(U_FAILURE(*pErrorCode)) {
1809 - udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
1810 - return 0;
1811 - }
1812 - }
1813 -
1814 - /* allocate the temporary table for sorting resource tables */
1815 - tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
1816 - if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY ) {
1817 - tempTable.rows=rows;
1818 - tempTable.resort=resort;
1819 - } else {
1820 - tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTab leLength*4);
1821 - if(tempTable.rows==NULL) {
1822 - udata_printError(ds, "ures_swap(): unable to allocate memory fo r sorting tables (max length: %d)\n",
1823 - maxTableLength);
1824 - *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1825 - if(tempTable.resFlags!=stackResFlags) {
1826 - uprv_free(tempTable.resFlags);
1827 - }
1828 - return 0;
1829 - }
1830 - tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
1831 - }
1832 -
1833 - /* swap the resources */
1834 - ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, p ErrorCode);
1835 - if(U_FAILURE(*pErrorCode)) {
1836 - udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
1837 - rootRes);
1838 - }
1839 -
1840 - if(tempTable.rows!=rows) {
1841 - uprv_free(tempTable.rows);
1842 - }
1843 - if(tempTable.resFlags!=stackResFlags) {
1844 - uprv_free(tempTable.resFlags);
1845 - }
1846 -
1847 - /* swap the root resource and indexes */
1848 - ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
1849 - }
1850 -
1851 - return headerSize+4*top;
1852 -}
1853 diff --git a/source/common/uresdata.cpp b/source/common/uresdata.cpp
1854 new file mode 100644
1855 index 0000000..fbfb56d
1856 --- /dev/null
1857 +++ b/source/common/uresdata.cpp
1858 @@ -0,0 +1,1466 @@
1859 +/*
1860 +*******************************************************************************
1861 +* Copyright (C) 1999-2015, International Business Machines Corporation
1862 +* and others. All Rights Reserved.
1863 +*******************************************************************************
1864 +* file name: uresdata.cpp
1865 +* encoding: US-ASCII
1866 +* tab size: 8 (not used)
1867 +* indentation:4
1868 +*
1869 +* created on: 1999dec08
1870 +* created by: Markus W. Scherer
1871 +* Modification History:
1872 +*
1873 +* Date Name Description
1874 +* 06/20/2000 helena OS/400 port changes; mostly typecast.
1875 +* 06/24/02 weiv Added support for resource sharing
1876 +*/
1877 +
1878 +#include "unicode/utypes.h"
1879 +#include "unicode/udata.h"
1880 +#include "unicode/ustring.h"
1881 +#include "unicode/utf16.h"
1882 +#include "cmemory.h"
1883 +#include "cstring.h"
1884 +#include "resource.h"
1885 +#include "uarrsort.h"
1886 +#include "uassert.h"
1887 +#include "ucol_swp.h"
1888 +#include "udataswp.h"
1889 +#include "uinvchar.h"
1890 +#include "uresdata.h"
1891 +#include "uresimp.h"
1892 +
1893 +/*
1894 + * Resource access helpers
1895 + */
1896 +
1897 +/* get a const char* pointer to the key with the keyOffset byte offset from pRo ot */
1898 +#define RES_GET_KEY16(pResData, keyOffset) \
1899 + ((keyOffset)<(pResData)->localKeyLimit ? \
1900 + (const char *)(pResData)->pRoot+(keyOffset) : \
1901 + (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit)
1902 +
1903 +#define RES_GET_KEY32(pResData, keyOffset) \
1904 + ((keyOffset)>=0 ? \
1905 + (const char *)(pResData)->pRoot+(keyOffset) : \
1906 + (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff))
1907 +
1908 +#define URESDATA_ITEM_NOT_FOUND -1
1909 +
1910 +/* empty resources, returned when the resource offset is 0 */
1911 +static const uint16_t gEmpty16=0;
1912 +
1913 +static const struct {
1914 + int32_t length;
1915 + int32_t res;
1916 +} gEmpty32={ 0, 0 };
1917 +
1918 +static const struct {
1919 + int32_t length;
1920 + UChar nul;
1921 + UChar pad;
1922 +} gEmptyString={ 0, 0, 0 };
1923 +
1924 +/*
1925 + * All the type-access functions assume that
1926 + * the resource is of the expected type.
1927 + */
1928 +
1929 +static int32_t
1930 +_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, in t32_t length,
1931 + const char *key, const char **realKey) {
1932 + const char *tableKey;
1933 + int32_t mid, start, limit;
1934 + int result;
1935 +
1936 + /* do a binary search for the key */
1937 + start=0;
1938 + limit=length;
1939 + while(start<limit) {
1940 + mid = (start + limit) / 2;
1941 + tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
1942 + if (pResData->useNativeStrcmp) {
1943 + result = uprv_strcmp(key, tableKey);
1944 + } else {
1945 + result = uprv_compareInvCharsAsAscii(key, tableKey);
1946 + }
1947 + if (result < 0) {
1948 + limit = mid;
1949 + } else if (result > 0) {
1950 + start = mid + 1;
1951 + } else {
1952 + /* We found it! */
1953 + *realKey=tableKey;
1954 + return mid;
1955 + }
1956 + }
1957 + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */
1958 +}
1959 +
1960 +static int32_t
1961 +_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, i nt32_t length,
1962 + const char *key, const char **realKey) {
1963 + const char *tableKey;
1964 + int32_t mid, start, limit;
1965 + int result;
1966 +
1967 + /* do a binary search for the key */
1968 + start=0;
1969 + limit=length;
1970 + while(start<limit) {
1971 + mid = (start + limit) / 2;
1972 + tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
1973 + if (pResData->useNativeStrcmp) {
1974 + result = uprv_strcmp(key, tableKey);
1975 + } else {
1976 + result = uprv_compareInvCharsAsAscii(key, tableKey);
1977 + }
1978 + if (result < 0) {
1979 + limit = mid;
1980 + } else if (result > 0) {
1981 + start = mid + 1;
1982 + } else {
1983 + /* We found it! */
1984 + *realKey=tableKey;
1985 + return mid;
1986 + }
1987 + }
1988 + return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */
1989 +}
1990 +
1991 +/* helper for res_load() ---------------------------------------------------- * /
1992 +
1993 +static UBool U_CALLCONV
1994 +isAcceptable(void *context,
1995 + const char * /*type*/, const char * /*name*/,
1996 + const UDataInfo *pInfo) {
1997 + uprv_memcpy(context, pInfo->formatVersion, 4);
1998 + return (UBool)(
1999 + pInfo->size>=20 &&
2000 + pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
2001 + pInfo->charsetFamily==U_CHARSET_FAMILY &&
2002 + pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
2003 + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
2004 + pInfo->dataFormat[1]==0x65 &&
2005 + pInfo->dataFormat[2]==0x73 &&
2006 + pInfo->dataFormat[3]==0x42 &&
2007 + (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3));
2008 +}
2009 +
2010 +/* semi-public functions ---------------------------------------------------- * /
2011 +
2012 +static void
2013 +res_init(ResourceData *pResData,
2014 + UVersionInfo formatVersion, const void *inBytes, int32_t length,
2015 + UErrorCode *errorCode) {
2016 + UResType rootType;
2017 +
2018 + /* get the root resource */
2019 + pResData->pRoot=(const int32_t *)inBytes;
2020 + pResData->rootRes=(Resource)*pResData->pRoot;
2021 + pResData->p16BitUnits=&gEmpty16;
2022 +
2023 + /* formatVersion 1.1 must have a root item and at least 5 indexes */
2024 + if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
2025 + *errorCode=U_INVALID_FORMAT_ERROR;
2026 + res_unload(pResData);
2027 + return;
2028 + }
2029 +
2030 + /* currently, we accept only resources that have a Table as their roots */
2031 + rootType=(UResType)RES_GET_TYPE(pResData->rootRes);
2032 + if(!URES_IS_TABLE(rootType)) {
2033 + *errorCode=U_INVALID_FORMAT_ERROR;
2034 + res_unload(pResData);
2035 + return;
2036 + }
2037 +
2038 + if(formatVersion[0]==1 && formatVersion[1]==0) {
2039 + pResData->localKeyLimit=0x10000; /* greater than any 16-bit key string offset */
2040 + } else {
2041 + /* bundles with formatVersion 1.1 and later contain an indexes[] array */
2042 + const int32_t *indexes=pResData->pRoot+1;
2043 + int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
2044 + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
2045 + *errorCode=U_INVALID_FORMAT_ERROR;
2046 + res_unload(pResData);
2047 + return;
2048 + }
2049 + if( length>=0 &&
2050 + (length<((1+indexLength)<<2) ||
2051 + length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
2052 + ) {
2053 + *errorCode=U_INVALID_FORMAT_ERROR;
2054 + res_unload(pResData);
2055 + return;
2056 + }
2057 + if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
2058 + pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
2059 + }
2060 + if(formatVersion[0]>=3) {
2061 + // In formatVersion 1, the indexLength took up this whole int.
2062 + // In version 2, bits 31..8 were reserved and always 0.
2063 + // In version 3, they contain bits 23..0 of the poolStringIndexLimi t.
2064 + // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12.
2065 + pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_IND EX_LENGTH]>>8);
2066 + }
2067 + if(indexLength>URES_INDEX_ATTRIBUTES) {
2068 + int32_t att=indexes[URES_INDEX_ATTRIBUTES];
2069 + pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK);
2070 + pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0);
2071 + pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0 );
2072 + pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 - > 27..24
2073 + pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16);
2074 + }
2075 + if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength< =URES_INDEX_POOL_CHECKSUM) {
2076 + *errorCode=U_INVALID_FORMAT_ERROR;
2077 + res_unload(pResData);
2078 + return;
2079 + }
2080 + if( indexLength>URES_INDEX_16BIT_TOP &&
2081 + indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
2082 + ) {
2083 + pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[UR ES_INDEX_KEYS_TOP]);
2084 + }
2085 + }
2086 +
2087 + if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) {
2088 + /*
2089 + * formatVersion 1: compare key strings in native-charset order
2090 + * formatVersion 2 and up: compare key strings in ASCII order
2091 + */
2092 + pResData->useNativeStrcmp=TRUE;
2093 + }
2094 +}
2095 +
2096 +U_CAPI void U_EXPORT2
2097 +res_read(ResourceData *pResData,
2098 + const UDataInfo *pInfo, const void *inBytes, int32_t length,
2099 + UErrorCode *errorCode) {
2100 + UVersionInfo formatVersion;
2101 +
2102 + uprv_memset(pResData, 0, sizeof(ResourceData));
2103 + if(U_FAILURE(*errorCode)) {
2104 + return;
2105 + }
2106 + if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) {
2107 + *errorCode=U_INVALID_FORMAT_ERROR;
2108 + return;
2109 + }
2110 + res_init(pResData, formatVersion, inBytes, length, errorCode);
2111 +}
2112 +
2113 +U_CFUNC void
2114 +res_load(ResourceData *pResData,
2115 + const char *path, const char *name, UErrorCode *errorCode) {
2116 + UVersionInfo formatVersion;
2117 +
2118 + uprv_memset(pResData, 0, sizeof(ResourceData));
2119 +
2120 + /* load the ResourceBundle file */
2121 + pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVers ion, errorCode);
2122 + if(U_FAILURE(*errorCode)) {
2123 + return;
2124 + }
2125 +
2126 + /* get its memory and initialize *pResData */
2127 + res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, erro rCode);
2128 +}
2129 +
2130 +U_CFUNC void
2131 +res_unload(ResourceData *pResData) {
2132 + if(pResData->data!=NULL) {
2133 + udata_close(pResData->data);
2134 + pResData->data=NULL;
2135 + }
2136 +}
2137 +
2138 +static const int8_t gPublicTypes[URES_LIMIT] = {
2139 + URES_STRING,
2140 + URES_BINARY,
2141 + URES_TABLE,
2142 + URES_ALIAS,
2143 +
2144 + URES_TABLE, /* URES_TABLE32 */
2145 + URES_TABLE, /* URES_TABLE16 */
2146 + URES_STRING, /* URES_STRING_V2 */
2147 + URES_INT,
2148 +
2149 + URES_ARRAY,
2150 + URES_ARRAY, /* URES_ARRAY16 */
2151 + URES_NONE,
2152 + URES_NONE,
2153 +
2154 + URES_NONE,
2155 + URES_NONE,
2156 + URES_INT_VECTOR,
2157 + URES_NONE
2158 +};
2159 +
2160 +U_CAPI UResType U_EXPORT2
2161 +res_getPublicType(Resource res) {
2162 + return (UResType)gPublicTypes[RES_GET_TYPE(res)];
2163 +}
2164 +
2165 +U_CAPI const UChar * U_EXPORT2
2166 +res_getString(const ResourceData *pResData, Resource res, int32_t *pLength) {
2167 + const UChar *p;
2168 + uint32_t offset=RES_GET_OFFSET(res);
2169 + int32_t length;
2170 + if(RES_GET_TYPE(res)==URES_STRING_V2) {
2171 + int32_t first;
2172 + if((int32_t)offset<pResData->poolStringIndexLimit) {
2173 + p=(const UChar *)pResData->poolBundleStrings+offset;
2174 + } else {
2175 + p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolString IndexLimit);
2176 + }
2177 + first=*p;
2178 + if(!U16_IS_TRAIL(first)) {
2179 + length=u_strlen(p);
2180 + } else if(first<0xdfef) {
2181 + length=first&0x3ff;
2182 + ++p;
2183 + } else if(first<0xdfff) {
2184 + length=((first-0xdfef)<<16)|p[1];
2185 + p+=2;
2186 + } else {
2187 + length=((int32_t)p[1]<<16)|p[2];
2188 + p+=3;
2189 + }
2190 + } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
2191 + const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res ;
2192 + length=*p32++;
2193 + p=(const UChar *)p32;
2194 + } else {
2195 + p=NULL;
2196 + length=0;
2197 + }
2198 + if(pLength) {
2199 + *pLength=length;
2200 + }
2201 + return p;
2202 +}
2203 +
2204 +namespace {
2205 +
2206 +/**
2207 + * CLDR string value (three empty-set symbols)=={2205, 2205, 2205}
2208 + * prevents fallback to the parent bundle.
2209 + * TODO: combine with other code that handles this marker, use EMPTY_SET consta nt.
2210 + * TODO: maybe move to uresbund.cpp?
2211 + */
2212 +UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) {
2213 + uint32_t offset=RES_GET_OFFSET(res);
2214 + if (offset == 0) {
2215 + // empty string
2216 + } else if (res == offset) {
2217 + const int32_t *p32=pResData->pRoot+res;
2218 + int32_t length=*p32;
2219 + const UChar *p=(const UChar *)p32;
2220 + return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x220 5;
2221 + } else if (RES_GET_TYPE(res) == URES_STRING_V2) {
2222 + const UChar *p;
2223 + if((int32_t)offset<pResData->poolStringIndexLimit) {
2224 + p=(const UChar *)pResData->poolBundleStrings+offset;
2225 + } else {
2226 + p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolString IndexLimit);
2227 + }
2228 + int32_t first=*p;
2229 + if (first == 0x2205) { // implicit length
2230 + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0;
2231 + } else if (first == 0xdc03) { // explicit length 3 (should not occur)
2232 + return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205;
2233 + } else {
2234 + // Assume that the string has not been stored with more length unit s than necessary.
2235 + return FALSE;
2236 + }
2237 + }
2238 + return FALSE;
2239 +}
2240 +
2241 +} // namespace
2242 +
2243 +U_CAPI const UChar * U_EXPORT2
2244 +res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
2245 + const UChar *p;
2246 + uint32_t offset=RES_GET_OFFSET(res);
2247 + int32_t length;
2248 + if(RES_GET_TYPE(res)==URES_ALIAS) {
2249 + const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+ offset;
2250 + length=*p32++;
2251 + p=(const UChar *)p32;
2252 + } else {
2253 + p=NULL;
2254 + length=0;
2255 + }
2256 + if(pLength) {
2257 + *pLength=length;
2258 + }
2259 + return p;
2260 +}
2261 +
2262 +U_CAPI const uint8_t * U_EXPORT2
2263 +res_getBinary(const ResourceData *pResData, Resource res, int32_t *pLength) {
2264 + const uint8_t *p;
2265 + uint32_t offset=RES_GET_OFFSET(res);
2266 + int32_t length;
2267 + if(RES_GET_TYPE(res)==URES_BINARY) {
2268 + const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->p Root+offset;
2269 + length=*p32++;
2270 + p=(const uint8_t *)p32;
2271 + } else {
2272 + p=NULL;
2273 + length=0;
2274 + }
2275 + if(pLength) {
2276 + *pLength=length;
2277 + }
2278 + return p;
2279 +}
2280 +
2281 +
2282 +U_CAPI const int32_t * U_EXPORT2
2283 +res_getIntVector(const ResourceData *pResData, Resource res, int32_t *pLength) {
2284 + const int32_t *p;
2285 + uint32_t offset=RES_GET_OFFSET(res);
2286 + int32_t length;
2287 + if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
2288 + p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset;
2289 + length=*p++;
2290 + } else {
2291 + p=NULL;
2292 + length=0;
2293 + }
2294 + if(pLength) {
2295 + *pLength=length;
2296 + }
2297 + return p;
2298 +}
2299 +
2300 +U_CAPI int32_t U_EXPORT2
2301 +res_countArrayItems(const ResourceData *pResData, Resource res) {
2302 + uint32_t offset=RES_GET_OFFSET(res);
2303 + switch(RES_GET_TYPE(res)) {
2304 + case URES_STRING:
2305 + case URES_STRING_V2:
2306 + case URES_BINARY:
2307 + case URES_ALIAS:
2308 + case URES_INT:
2309 + case URES_INT_VECTOR:
2310 + return 1;
2311 + case URES_ARRAY:
2312 + case URES_TABLE32:
2313 + return offset==0 ? 0 : *(pResData->pRoot+offset);
2314 + case URES_TABLE:
2315 + return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
2316 + case URES_ARRAY16:
2317 + case URES_TABLE16:
2318 + return pResData->p16BitUnits[offset];
2319 + default:
2320 + return 0;
2321 + }
2322 +}
2323 +
2324 +namespace {
2325 +
2326 +int32_t getArrayLength(const ResourceData *pResData, Resource res) {
2327 + uint32_t offset=RES_GET_OFFSET(res);
2328 + if(offset == 0) {
2329 + return 0;
2330 + }
2331 + int32_t type = RES_GET_TYPE(res);
2332 + if(type == URES_ARRAY) {
2333 + return *(pResData->pRoot+offset);
2334 + } else if(type == URES_ARRAY16) {
2335 + return pResData->p16BitUnits[offset];
2336 + } else {
2337 + return 0;
2338 + }
2339 +}
2340 +
2341 +int32_t getTableLength(const ResourceData *pResData, Resource res) {
2342 + uint32_t offset=RES_GET_OFFSET(res);
2343 + if(offset == 0) {
2344 + return 0;
2345 + }
2346 + int32_t type = RES_GET_TYPE(res);
2347 + if(type == URES_TABLE) {
2348 + return *((const uint16_t *)(pResData->pRoot+offset));
2349 + } else if(type == URES_TABLE16) {
2350 + return pResData->p16BitUnits[offset];
2351 + } else if(type == URES_TABLE32) {
2352 + return *(pResData->pRoot+offset);
2353 + } else {
2354 + return 0;
2355 + }
2356 +}
2357 +
2358 +} // namespace
2359 +
2360 +U_NAMESPACE_BEGIN
2361 +
2362 +ResourceDataValue::~ResourceDataValue() {}
2363 +
2364 +UResType ResourceDataValue::getType() const {
2365 + return res_getPublicType(res);
2366 +}
2367 +
2368 +const UChar *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCod e) const {
2369 + if(U_FAILURE(errorCode)) {
2370 + return NULL;
2371 + }
2372 + const UChar *s = res_getString(pResData, res, &length);
2373 + if(s == NULL) {
2374 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2375 + }
2376 + return s;
2377 +}
2378 +
2379 +const UChar *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &err orCode) const {
2380 + if(U_FAILURE(errorCode)) {
2381 + return NULL;
2382 + }
2383 + const UChar *s = res_getAlias(pResData, res, &length);
2384 + if(s == NULL) {
2385 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2386 + }
2387 + return s;
2388 +}
2389 +
2390 +int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const {
2391 + if(U_FAILURE(errorCode)) {
2392 + return 0;
2393 + }
2394 + if(RES_GET_TYPE(res) != URES_INT) {
2395 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2396 + }
2397 + return RES_GET_INT(res);
2398 +}
2399 +
2400 +uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const {
2401 + if(U_FAILURE(errorCode)) {
2402 + return 0;
2403 + }
2404 + if(RES_GET_TYPE(res) != URES_INT) {
2405 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2406 + }
2407 + return RES_GET_UINT(res);
2408 +}
2409 +
2410 +const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &err orCode) const {
2411 + if(U_FAILURE(errorCode)) {
2412 + return NULL;
2413 + }
2414 + const int32_t *iv = res_getIntVector(pResData, res, &length);
2415 + if(iv == NULL) {
2416 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2417 + }
2418 + return iv;
2419 +}
2420 +
2421 +const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorC ode) const {
2422 + if(U_FAILURE(errorCode)) {
2423 + return NULL;
2424 + }
2425 + const uint8_t *b = res_getBinary(pResData, res, &length);
2426 + if(b == NULL) {
2427 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2428 + }
2429 + return b;
2430 +}
2431 +
2432 +U_NAMESPACE_END
2433 +
2434 +static Resource
2435 +makeResourceFrom16(const ResourceData *pResData, int32_t res16) {
2436 + if(res16<pResData->poolStringIndex16Limit) {
2437 + // Pool string, nothing to do.
2438 + } else {
2439 + // Local string, adjust the 16-bit offset to a regular one,
2440 + // with a larger pool string index limit.
2441 + res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexL imit;
2442 + }
2443 + return URES_MAKE_RESOURCE(URES_STRING_V2, res16);
2444 +}
2445 +
2446 +U_CAPI Resource U_EXPORT2
2447 +res_getTableItemByKey(const ResourceData *pResData, Resource table,
2448 + int32_t *indexR, const char **key) {
2449 + uint32_t offset=RES_GET_OFFSET(table);
2450 + int32_t length;
2451 + int32_t idx;
2452 + if(key == NULL || *key == NULL) {
2453 + return RES_BOGUS;
2454 + }
2455 + switch(RES_GET_TYPE(table)) {
2456 + case URES_TABLE: {
2457 + if (offset!=0) { /* empty if offset==0 */
2458 + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
2459 + length=*p++;
2460 + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
2461 + if(idx>=0) {
2462 + const Resource *p32=(const Resource *)(p+length+(~length&1));
2463 + return p32[idx];
2464 + }
2465 + }
2466 + break;
2467 + }
2468 + case URES_TABLE16: {
2469 + const uint16_t *p=pResData->p16BitUnits+offset;
2470 + length=*p++;
2471 + *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
2472 + if(idx>=0) {
2473 + return makeResourceFrom16(pResData, p[length+idx]);
2474 + }
2475 + break;
2476 + }
2477 + case URES_TABLE32: {
2478 + if (offset!=0) { /* empty if offset==0 */
2479 + const int32_t *p= pResData->pRoot+offset;
2480 + length=*p++;
2481 + *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
2482 + if(idx>=0) {
2483 + return (Resource)p[length+idx];
2484 + }
2485 + }
2486 + break;
2487 + }
2488 + default:
2489 + break;
2490 + }
2491 + return RES_BOGUS;
2492 +}
2493 +
2494 +U_CAPI Resource U_EXPORT2
2495 +res_getTableItemByIndex(const ResourceData *pResData, Resource table,
2496 + int32_t indexR, const char **key) {
2497 + uint32_t offset=RES_GET_OFFSET(table);
2498 + int32_t length;
2499 + U_ASSERT(indexR>=0); /* to ensure the index is not negative */
2500 + switch(RES_GET_TYPE(table)) {
2501 + case URES_TABLE: {
2502 + if (offset != 0) { /* empty if offset==0 */
2503 + const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
2504 + length=*p++;
2505 + if(indexR<length) {
2506 + const Resource *p32=(const Resource *)(p+length+(~length&1));
2507 + if(key!=NULL) {
2508 + *key=RES_GET_KEY16(pResData, p[indexR]);
2509 + }
2510 + return p32[indexR];
2511 + }
2512 + }
2513 + break;
2514 + }
2515 + case URES_TABLE16: {
2516 + const uint16_t *p=pResData->p16BitUnits+offset;
2517 + length=*p++;
2518 + if(indexR<length) {
2519 + if(key!=NULL) {
2520 + *key=RES_GET_KEY16(pResData, p[indexR]);
2521 + }
2522 + return makeResourceFrom16(pResData, p[length+indexR]);
2523 + }
2524 + break;
2525 + }
2526 + case URES_TABLE32: {
2527 + if (offset != 0) { /* empty if offset==0 */
2528 + const int32_t *p= pResData->pRoot+offset;
2529 + length=*p++;
2530 + if(indexR<length) {
2531 + if(key!=NULL) {
2532 + *key=RES_GET_KEY32(pResData, p[indexR]);
2533 + }
2534 + return (Resource)p[length+indexR];
2535 + }
2536 + }
2537 + break;
2538 + }
2539 + default:
2540 + break;
2541 + }
2542 + return RES_BOGUS;
2543 +}
2544 +
2545 +U_CAPI Resource U_EXPORT2
2546 +res_getResource(const ResourceData *pResData, const char *key) {
2547 + const char *realKey=key;
2548 + int32_t idx;
2549 + return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
2550 +}
2551 +
2552 +// TODO: Ported from Java, but enumerating at this low level may prevent us
2553 +// from doing necessary things, like resolving aliases,
2554 +// which need access to higher-level UResourceBundle code.
2555 +// Consider porting the low-level Container/Array/Table classes from Java,
2556 +// with getters for keys and values,
2557 +// and doing the enumeration in the higher-level code on top of those accessors .
2558 +U_CFUNC void
2559 +ures_getAllTableItems(const ResourceData *pResData, Resource table,
2560 + icu::ResourceDataValue &value, icu::ResourceTableSink &si nk,
2561 + UErrorCode &errorCode) {
2562 + if(U_FAILURE(errorCode)) { return; }
2563 + const uint16_t *keys16 = NULL;
2564 + const int32_t *keys32 = NULL;
2565 + const uint16_t *items16 = NULL;
2566 + const Resource *items32 = NULL;
2567 + uint32_t offset = RES_GET_OFFSET(table);
2568 + int32_t length = 0;
2569 + switch(RES_GET_TYPE(table)) {
2570 + case URES_TABLE: {
2571 + if (offset != 0) { /* empty if offset==0 */
2572 + keys16 = (const uint16_t *)(pResData->pRoot+offset);
2573 + length = *keys16++;
2574 + items32 = (const Resource *)(keys16+length+(~length&1));
2575 + }
2576 + break;
2577 + }
2578 + case URES_TABLE16: {
2579 + keys16 = pResData->p16BitUnits+offset;
2580 + length = *keys16++;
2581 + items16 = keys16 + length;
2582 + break;
2583 + }
2584 + case URES_TABLE32: {
2585 + if (offset != 0) { /* empty if offset==0 */
2586 + keys32 = pResData->pRoot+offset;
2587 + length = *keys32++;
2588 + items32 = (const Resource *)keys32 + length;
2589 + }
2590 + break;
2591 + }
2592 + default:
2593 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2594 + return;
2595 + }
2596 +
2597 + for (int32_t i = 0; i < length; ++i) {
2598 + const char *key;
2599 + if (keys16 != NULL) {
2600 + key=RES_GET_KEY16(pResData, keys16[i]);
2601 + } else {
2602 + key=RES_GET_KEY32(pResData, keys32[i]);
2603 + }
2604 + Resource res;
2605 + if (items16 != NULL) {
2606 + res = makeResourceFrom16(pResData, items16[i]);
2607 + } else {
2608 + res = items32[i];
2609 + }
2610 + int32_t type = RES_GET_TYPE(res);
2611 + if (URES_IS_ARRAY(type)) {
2612 + int32_t numItems = getArrayLength(pResData, res);
2613 + icu::ResourceArraySink *subSink = sink.getOrCreateArraySink(key, nu mItems, errorCode);
2614 + if (subSink != NULL) {
2615 + ures_getAllArrayItems(pResData, res, value, *subSink, errorCode );
2616 + }
2617 + } else if (URES_IS_TABLE(type)) {
2618 + int32_t numItems = getTableLength(pResData, res);
2619 + icu::ResourceTableSink *subSink = sink.getOrCreateTableSink(key, nu mItems, errorCode);
2620 + if (subSink != NULL) {
2621 + ures_getAllTableItems(pResData, res, value, *subSink, errorCode );
2622 + }
2623 + /* TODO: settle on how to deal with aliases, port to Java
2624 + } else if (type == URES_ALIAS) {
2625 + // aliases not handled in resource enumeration
2626 + errorCode = U_UNSUPPORTED_ERROR;
2627 + return; */
2628 + } else if (isNoInheritanceMarker(pResData, res)) {
2629 + sink.putNoFallback(key, errorCode);
2630 + } else {
2631 + value.setResource(res);
2632 + sink.put(key, value, errorCode);
2633 + }
2634 + if(U_FAILURE(errorCode)) { return; }
2635 + }
2636 + sink.leave(errorCode);
2637 +}
2638 +
2639 +U_CAPI Resource U_EXPORT2
2640 +res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
2641 + uint32_t offset=RES_GET_OFFSET(array);
2642 + U_ASSERT(indexR>=0); /* to ensure the index is not negative */
2643 + switch(RES_GET_TYPE(array)) {
2644 + case URES_ARRAY: {
2645 + if (offset!=0) { /* empty if offset==0 */
2646 + const int32_t *p= pResData->pRoot+offset;
2647 + if(indexR<*p) {
2648 + return (Resource)p[1+indexR];
2649 + }
2650 + }
2651 + break;
2652 + }
2653 + case URES_ARRAY16: {
2654 + const uint16_t *p=pResData->p16BitUnits+offset;
2655 + if(indexR<*p) {
2656 + return makeResourceFrom16(pResData, p[1+indexR]);
2657 + }
2658 + break;
2659 + }
2660 + default:
2661 + break;
2662 + }
2663 + return RES_BOGUS;
2664 +}
2665 +
2666 +U_CFUNC void
2667 +ures_getAllArrayItems(const ResourceData *pResData, Resource array,
2668 + icu::ResourceDataValue &value, icu::ResourceArraySink &si nk,
2669 + UErrorCode &errorCode) {
2670 + if(U_FAILURE(errorCode)) { return; }
2671 + const uint16_t *items16 = NULL;
2672 + const Resource *items32 = NULL;
2673 + uint32_t offset=RES_GET_OFFSET(array);
2674 + int32_t length = 0;
2675 + switch(RES_GET_TYPE(array)) {
2676 + case URES_ARRAY: {
2677 + if (offset!=0) { /* empty if offset==0 */
2678 + items32 = (const Resource *)pResData->pRoot+offset;
2679 + length = *items32++;
2680 + }
2681 + break;
2682 + }
2683 + case URES_ARRAY16: {
2684 + items16 = pResData->p16BitUnits+offset;
2685 + length = *items16++;
2686 + break;
2687 + }
2688 + default:
2689 + errorCode = U_RESOURCE_TYPE_MISMATCH;
2690 + return;
2691 + }
2692 +
2693 + for (int32_t i = 0; i < length; ++i) {
2694 + Resource res;
2695 + if (items16 != NULL) {
2696 + res = makeResourceFrom16(pResData, items16[i]);
2697 + } else {
2698 + res = items32[i];
2699 + }
2700 + int32_t type = RES_GET_TYPE(res);
2701 + if (URES_IS_ARRAY(type)) {
2702 + int32_t numItems = getArrayLength(pResData, res);
2703 + icu::ResourceArraySink *subSink = sink.getOrCreateArraySink(i, numI tems, errorCode);
2704 + if (subSink != NULL) {
2705 + ures_getAllArrayItems(pResData, res, value, *subSink, errorCode );
2706 + }
2707 + } else if (URES_IS_TABLE(type)) {
2708 + int32_t numItems = getTableLength(pResData, res);
2709 + icu::ResourceTableSink *subSink = sink.getOrCreateTableSink(i, numI tems, errorCode);
2710 + if (subSink != NULL) {
2711 + ures_getAllTableItems(pResData, res, value, *subSink, errorCode );
2712 + }
2713 + /* TODO: settle on how to deal with aliases, port to Java
2714 + } else if (type == URES_ALIAS) {
2715 + // aliases not handled in resource enumeration
2716 + errorCode = U_UNSUPPORTED_ERROR;
2717 + return; */
2718 + } else {
2719 + value.setResource(res);
2720 + sink.put(i, value, errorCode);
2721 + }
2722 + if(U_FAILURE(errorCode)) { return; }
2723 + }
2724 + sink.leave(errorCode);
2725 +}
2726 +
2727 +U_CFUNC Resource
2728 +res_findResource(const ResourceData *pResData, Resource r, char** path, const c har** key) {
2729 + char *pathP = *path, *nextSepP = *path;
2730 + char *closeIndex = NULL;
2731 + Resource t1 = r;
2732 + Resource t2;
2733 + int32_t indexR = 0;
2734 + UResType type = (UResType)RES_GET_TYPE(t1);
2735 +
2736 + /* if you come in with an empty path, you'll be getting back the same resourc e */
2737 + if(!uprv_strlen(pathP)) {
2738 + return r;
2739 + }
2740 +
2741 + /* one needs to have an aggregate resource in order to search in it */
2742 + if(!URES_IS_CONTAINER(type)) {
2743 + return RES_BOGUS;
2744 + }
2745 +
2746 + while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) {
2747 + /* Iteration stops if: the path has been consumed, we found a non-existing
2748 + * resource (t1 == RES_BOGUS) or we found a scalar resource (including alia s)
2749 + */
2750 + nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR);
2751 + /* if there are more separators, terminate string
2752 + * and set path to the remaining part of the string
2753 + */
2754 + if(nextSepP != NULL) {
2755 + if(nextSepP == pathP) {
2756 + // Empty key string.
2757 + return RES_BOGUS;
2758 + }
2759 + *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
2760 + *path = nextSepP+1;
2761 + } else {
2762 + *path = uprv_strchr(pathP, 0);
2763 + }
2764 +
2765 + /* if the resource is a table */
2766 + /* try the key based access */
2767 + if(URES_IS_TABLE(type)) {
2768 + *key = pathP;
2769 + t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
2770 + if(t2 == RES_BOGUS) {
2771 + /* if we fail to get the resource by key, maybe we got an index */
2772 + indexR = uprv_strtol(pathP, &closeIndex, 10);
2773 + if(*closeIndex == 0) {
2774 + /* if we indeed have an index, try to get the item by index */
2775 + t2 = res_getTableItemByIndex(pResData, t1, indexR, key);
2776 + }
2777 + }
2778 + } else if(URES_IS_ARRAY(type)) {
2779 + indexR = uprv_strtol(pathP, &closeIndex, 10);
2780 + if(*closeIndex == 0) {
2781 + t2 = res_getArrayItem(pResData, t1, indexR);
2782 + } else {
2783 + t2 = RES_BOGUS; /* have an array, but don't have a valid index */
2784 + }
2785 + *key = NULL;
2786 + } else { /* can't do much here, except setting t2 to bogus */
2787 + t2 = RES_BOGUS;
2788 + }
2789 + t1 = t2;
2790 + type = (UResType)RES_GET_TYPE(t1);
2791 + /* position pathP to next resource key/index */
2792 + pathP = *path;
2793 + }
2794 +
2795 + return t1;
2796 +}
2797 +
2798 +/* resource bundle swapping ------------------------------------------------- * /
2799 +
2800 +/*
2801 + * Need to always enumerate the entire item tree,
2802 + * track the lowest address of any item to use as the limit for char keys[],
2803 + * track the highest address of any item to return the size of the data.
2804 + *
2805 + * We should have thought of storing those in the data...
2806 + * It is possible to extend the data structure by putting additional values
2807 + * in places that are inaccessible by ordinary enumeration of the item tree.
2808 + * For example, additional integers could be stored at the beginning or
2809 + * end of the key strings; this could be indicated by a minor version number,
2810 + * and the data swapping would have to know about these values.
2811 + *
2812 + * The data structure does not forbid keys to be shared, so we must swap
2813 + * all keys once instead of each key when it is referenced.
2814 + *
2815 + * These swapping functions assume that a resource bundle always has a length
2816 + * that is a multiple of 4 bytes.
2817 + * Currently, this is trivially true because genrb writes bundle tree leaves
2818 + * physically first, before their branches, so that the root table with its
2819 + * array of resource items (uint32_t values) is always last.
2820 + */
2821 +
2822 +/* definitions for table sorting ------------------------ */
2823 +
2824 +/*
2825 + * row of a temporary array
2826 + *
2827 + * gets platform-endian key string indexes and sorting indexes;
2828 + * after sorting this array by keys, the actual key/value arrays are permutated
2829 + * according to the sorting indexes
2830 + */
2831 +typedef struct Row {
2832 + int32_t keyIndex, sortIndex;
2833 +} Row;
2834 +
2835 +static int32_t
2836 +ures_compareRows(const void *context, const void *left, const void *right) {
2837 + const char *keyChars=(const char *)context;
2838 + return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex,
2839 + keyChars+((const Row *)right)->keyIndex);
2840 +}
2841 +
2842 +typedef struct TempTable {
2843 + const char *keyChars;
2844 + Row *rows;
2845 + int32_t *resort;
2846 + uint32_t *resFlags;
2847 + int32_t localKeyLimit;
2848 + uint8_t majorFormatVersion;
2849 +} TempTable;
2850 +
2851 +enum {
2852 + STACK_ROW_CAPACITY=200
2853 +};
2854 +
2855 +/* The table item key string is not locally available. */
2856 +static const char *const gUnknownKey="";
2857 +
2858 +/* resource table key for collation binaries: "%%CollationBin" */
2859 +static const UChar gCollationBinKey[]={
2860 + 0x25, 0x25,
2861 + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
2862 + 0x42, 0x69, 0x6e,
2863 + 0
2864 +};
2865 +
2866 +/*
2867 + * swap one resource item
2868 + */
2869 +static void
2870 +ures_swapResource(const UDataSwapper *ds,
2871 + const Resource *inBundle, Resource *outBundle,
2872 + Resource res, /* caller swaps res itself */
2873 + const char *key,
2874 + TempTable *pTempTable,
2875 + UErrorCode *pErrorCode) {
2876 + const Resource *p;
2877 + Resource *q;
2878 + int32_t offset, count;
2879 +
2880 + switch(RES_GET_TYPE(res)) {
2881 + case URES_TABLE16:
2882 + case URES_STRING_V2:
2883 + case URES_INT:
2884 + case URES_ARRAY16:
2885 + /* integer, or points to 16-bit units, nothing to do here */
2886 + return;
2887 + default:
2888 + break;
2889 + }
2890 +
2891 + /* all other types use an offset to point to their data */
2892 + offset=(int32_t)RES_GET_OFFSET(res);
2893 + if(offset==0) {
2894 + /* special offset indicating an empty item */
2895 + return;
2896 + }
2897 + if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) {
2898 + /* we already swapped this resource item */
2899 + return;
2900 + } else {
2901 + /* mark it as swapped now */
2902 + pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f));
2903 + }
2904 +
2905 + p=inBundle+offset;
2906 + q=outBundle+offset;
2907 +
2908 + switch(RES_GET_TYPE(res)) {
2909 + case URES_ALIAS:
2910 + /* physically same value layout as string, fall through */
2911 + case URES_STRING:
2912 + count=udata_readInt32(ds, (int32_t)*p);
2913 + /* swap length */
2914 + ds->swapArray32(ds, p, 4, q, pErrorCode);
2915 + /* swap each UChar (the terminating NUL would not change) */
2916 + ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
2917 + break;
2918 + case URES_BINARY:
2919 + count=udata_readInt32(ds, (int32_t)*p);
2920 + /* swap length */
2921 + ds->swapArray32(ds, p, 4, q, pErrorCode);
2922 + /* no need to swap or copy bytes - ures_swap() copied them all */
2923 +
2924 + /* swap known formats */
2925 +#if !UCONFIG_NO_COLLATION
2926 + if( key!=NULL && /* the binary is in a table */
2927 + (key!=gUnknownKey ?
2928 + /* its table key string is "%%CollationBin" */
2929 + 0==ds->compareInvChars(ds, key, -1,
2930 + gCollationBinKey, UPRV_LENGTHOF(gCollati onBinKey)-1) :
2931 + /* its table key string is unknown but it looks like a collatio n binary */
2932 + ucol_looksLikeCollationBinary(ds, p+1, count))
2933 + ) {
2934 + ucol_swap(ds, p+1, count, q+1, pErrorCode);
2935 + }
2936 +#endif
2937 + break;
2938 + case URES_TABLE:
2939 + case URES_TABLE32:
2940 + {
2941 + const uint16_t *pKey16;
2942 + uint16_t *qKey16;
2943 +
2944 + const int32_t *pKey32;
2945 + int32_t *qKey32;
2946 +
2947 + Resource item;
2948 + int32_t i, oldIndex;
2949 +
2950 + if(RES_GET_TYPE(res)==URES_TABLE) {
2951 + /* get table item count */
2952 + pKey16=(const uint16_t *)p;
2953 + qKey16=(uint16_t *)q;
2954 + count=ds->readUInt16(*pKey16);
2955 +
2956 + pKey32=qKey32=NULL;
2957 +
2958 + /* swap count */
2959 + ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
2960 +
2961 + offset+=((1+count)+1)/2;
2962 + } else {
2963 + /* get table item count */
2964 + pKey32=(const int32_t *)p;
2965 + qKey32=(int32_t *)q;
2966 + count=udata_readInt32(ds, *pKey32);
2967 +
2968 + pKey16=qKey16=NULL;
2969 +
2970 + /* swap count */
2971 + ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
2972 +
2973 + offset+=1+count;
2974 + }
2975 +
2976 + if(count==0) {
2977 + break;
2978 + }
2979 +
2980 + p=inBundle+offset; /* pointer to table resources */
2981 + q=outBundle+offset;
2982 +
2983 + /* recurse */
2984 + for(i=0; i<count; ++i) {
2985 + const char *itemKey=gUnknownKey;
2986 + if(pKey16!=NULL) {
2987 + int32_t keyOffset=ds->readUInt16(pKey16[i]);
2988 + if(keyOffset<pTempTable->localKeyLimit) {
2989 + itemKey=(const char *)outBundle+keyOffset;
2990 + }
2991 + } else {
2992 + int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
2993 + if(keyOffset>=0) {
2994 + itemKey=(const char *)outBundle+keyOffset;
2995 + }
2996 + }
2997 + item=ds->readUInt32(p[i]);
2998 + ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTemp Table, pErrorCode);
2999 + if(U_FAILURE(*pErrorCode)) {
3000 + udata_printError(ds, "ures_swapResource(table res=%08x)[%d] .recurse(%08x) failed\n",
3001 + res, i, item);
3002 + return;
3003 + }
3004 + }
3005 +
3006 + if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharse t) {
3007 + /* no need to sort, just swap the offset/value arrays */
3008 + if(pKey16!=NULL) {
3009 + ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
3010 + ds->swapArray32(ds, p, count*4, q, pErrorCode);
3011 + } else {
3012 + /* swap key offsets and items as one array */
3013 + ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
3014 + }
3015 + break;
3016 + }
3017 +
3018 + /*
3019 + * We need to sort tables by outCharset key strings because they
3020 + * sort differently for different charset families.
3021 + * ures_swap() already set pTempTable->keyChars appropriately.
3022 + * First we set up a temporary table with the key indexes and
3023 + * sorting indexes and sort that.
3024 + * Then we permutate and copy/swap the actual values.
3025 + */
3026 + if(pKey16!=NULL) {
3027 + for(i=0; i<count; ++i) {
3028 + pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
3029 + pTempTable->rows[i].sortIndex=i;
3030 + }
3031 + } else {
3032 + for(i=0; i<count; ++i) {
3033 + pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]) ;
3034 + pTempTable->rows[i].sortIndex=i;
3035 + }
3036 + }
3037 + uprv_sortArray(pTempTable->rows, count, sizeof(Row),
3038 + ures_compareRows, pTempTable->keyChars,
3039 + FALSE, pErrorCode);
3040 + if(U_FAILURE(*pErrorCode)) {
3041 + udata_printError(ds, "ures_swapResource(table res=%08x).uprv_so rtArray(%d items) failed\n",
3042 + res, count);
3043 + return;
3044 + }
3045 +
3046 + /*
3047 + * copy/swap/permutate items
3048 + *
3049 + * If we swap in-place, then the permutation must use another
3050 + * temporary array (pTempTable->resort)
3051 + * before the results are copied to the outBundle.
3052 + */
3053 + /* keys */
3054 + if(pKey16!=NULL) {
3055 + uint16_t *rKey16;
3056 +
3057 + if(pKey16!=qKey16) {
3058 + rKey16=qKey16;
3059 + } else {
3060 + rKey16=(uint16_t *)pTempTable->resort;
3061 + }
3062 + for(i=0; i<count; ++i) {
3063 + oldIndex=pTempTable->rows[i].sortIndex;
3064 + ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCod e);
3065 + }
3066 + if(qKey16!=rKey16) {
3067 + uprv_memcpy(qKey16, rKey16, 2*count);
3068 + }
3069 + } else {
3070 + int32_t *rKey32;
3071 +
3072 + if(pKey32!=qKey32) {
3073 + rKey32=qKey32;
3074 + } else {
3075 + rKey32=pTempTable->resort;
3076 + }
3077 + for(i=0; i<count; ++i) {
3078 + oldIndex=pTempTable->rows[i].sortIndex;
3079 + ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCod e);
3080 + }
3081 + if(qKey32!=rKey32) {
3082 + uprv_memcpy(qKey32, rKey32, 4*count);
3083 + }
3084 + }
3085 +
3086 + /* resources */
3087 + {
3088 + Resource *r;
3089 +
3090 +
3091 + if(p!=q) {
3092 + r=q;
3093 + } else {
3094 + r=(Resource *)pTempTable->resort;
3095 + }
3096 + for(i=0; i<count; ++i) {
3097 + oldIndex=pTempTable->rows[i].sortIndex;
3098 + ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
3099 + }
3100 + if(q!=r) {
3101 + uprv_memcpy(q, r, 4*count);
3102 + }
3103 + }
3104 + }
3105 + break;
3106 + case URES_ARRAY:
3107 + {
3108 + Resource item;
3109 + int32_t i;
3110 +
3111 + count=udata_readInt32(ds, (int32_t)*p);
3112 + /* swap length */
3113 + ds->swapArray32(ds, p++, 4, q++, pErrorCode);
3114 +
3115 + /* recurse */
3116 + for(i=0; i<count; ++i) {
3117 + item=ds->readUInt32(p[i]);
3118 + ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTab le, pErrorCode);
3119 + if(U_FAILURE(*pErrorCode)) {
3120 + udata_printError(ds, "ures_swapResource(array res=%08x)[%d] .recurse(%08x) failed\n",
3121 + res, i, item);
3122 + return;
3123 + }
3124 + }
3125 +
3126 + /* swap items */
3127 + ds->swapArray32(ds, p, 4*count, q, pErrorCode);
3128 + }
3129 + break;
3130 + case URES_INT_VECTOR:
3131 + count=udata_readInt32(ds, (int32_t)*p);
3132 + /* swap length and each integer */
3133 + ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
3134 + break;
3135 + default:
3136 + /* also catches RES_BOGUS */
3137 + *pErrorCode=U_UNSUPPORTED_ERROR;
3138 + break;
3139 + }
3140 +}
3141 +
3142 +U_CAPI int32_t U_EXPORT2
3143 +ures_swap(const UDataSwapper *ds,
3144 + const void *inData, int32_t length, void *outData,
3145 + UErrorCode *pErrorCode) {
3146 + const UDataInfo *pInfo;
3147 + const Resource *inBundle;
3148 + Resource rootRes;
3149 + int32_t headerSize, maxTableLength;
3150 +
3151 + Row rows[STACK_ROW_CAPACITY];
3152 + int32_t resort[STACK_ROW_CAPACITY];
3153 + TempTable tempTable;
3154 +
3155 + const int32_t *inIndexes;
3156 +
3157 + /* the following integers count Resource item offsets (4 bytes each), not b ytes */
3158 + int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top;
3159 +
3160 + /* udata_swapDataHeader checks the arguments */
3161 + headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
3162 + if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
3163 + return 0;
3164 + }
3165 +
3166 + /* check data format and format version */
3167 + pInfo=(const UDataInfo *)((const char *)inData+4);
3168 + if(!(
3169 + pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
3170 + pInfo->dataFormat[1]==0x65 &&
3171 + pInfo->dataFormat[2]==0x73 &&
3172 + pInfo->dataFormat[3]==0x42 &&
3173 + /* formatVersion 1.1+ or 2.x or 3.x */
3174 + ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||
3175 + pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3)
3176 + )) {
3177 + udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (for mat version %02x.%02x) is not a resource bundle\n",
3178 + pInfo->dataFormat[0], pInfo->dataFormat[1],
3179 + pInfo->dataFormat[2], pInfo->dataFormat[3],
3180 + pInfo->formatVersion[0], pInfo->formatVersion[1]);
3181 + *pErrorCode=U_UNSUPPORTED_ERROR;
3182 + return 0;
3183 + }
3184 + tempTable.majorFormatVersion=pInfo->formatVersion[0];
3185 +
3186 + /* a resource bundle must contain at least one resource item */
3187 + if(length<0) {
3188 + bundleLength=-1;
3189 + } else {
3190 + bundleLength=(length-headerSize)/4;
3191 +
3192 + /* formatVersion 1.1 must have a root item and at least 5 indexes */
3193 + if(bundleLength<(1+5)) {
3194 + udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
3195 + length-headerSize);
3196 + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
3197 + return 0;
3198 + }
3199 + }
3200 +
3201 + inBundle=(const Resource *)((const char *)inData+headerSize);
3202 + rootRes=ds->readUInt32(*inBundle);
3203 +
3204 + /* formatVersion 1.1 adds the indexes[] array */
3205 + inIndexes=(const int32_t *)(inBundle+1);
3206 +
3207 + indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
3208 + if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
3209 + udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
3210 + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
3211 + return 0;
3212 + }
3213 + keysBottom=1+indexLength;
3214 + keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
3215 + if(indexLength>URES_INDEX_16BIT_TOP) {
3216 + resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
3217 + } else {
3218 + resBottom=keysTop;
3219 + }
3220 + top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
3221 + maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
3222 +
3223 + if(0<=bundleLength && bundleLength<top) {
3224 + udata_printError(ds, "ures_swap(): resource top %d exceeds bundle lengt h %d\n",
3225 + top, bundleLength);
3226 + *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
3227 + return 0;
3228 + }
3229 + if(keysTop>(1+indexLength)) {
3230 + tempTable.localKeyLimit=keysTop<<2;
3231 + } else {
3232 + tempTable.localKeyLimit=0;
3233 + }
3234 +
3235 + if(length>=0) {
3236 + Resource *outBundle=(Resource *)((char *)outData+headerSize);
3237 +
3238 + /* track which resources we have already swapped */
3239 + uint32_t stackResFlags[STACK_ROW_CAPACITY];
3240 + int32_t resFlagsLength;
3241 +
3242 + /*
3243 + * We need one bit per 4 resource bundle bytes so that we can track
3244 + * every possible Resource for whether we have swapped it already.
3245 + * Multiple Resource words can refer to the same bundle offsets
3246 + * for sharing identical values.
3247 + * We could optimize this by allocating only for locations above
3248 + * where Resource values are stored (above keys & strings).
3249 + */
3250 + resFlagsLength=(length+31)>>5; /* number of bytes needed */
3251 + resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint 32_t */
3252 + if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) {
3253 + tempTable.resFlags=stackResFlags;
3254 + } else {
3255 + tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
3256 + if(tempTable.resFlags==NULL) {
3257 + udata_printError(ds, "ures_swap(): unable to allocate memory fo r tracking resources\n");
3258 + *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
3259 + return 0;
3260 + }
3261 + }
3262 + uprv_memset(tempTable.resFlags, 0, resFlagsLength);
3263 +
3264 + /* copy the bundle for binary and inaccessible data */
3265 + if(inData!=outData) {
3266 + uprv_memcpy(outBundle, inBundle, 4*top);
3267 + }
3268 +
3269 + /* swap the key strings, but not the padding bytes (0xaa) after the las t string and its NUL */
3270 + udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom ),
3271 + outBundle+keysBottom, pErrorCode);
3272 + if(U_FAILURE(*pErrorCode)) {
3273 + udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d] ) failed\n", 4*(keysTop-keysBottom));
3274 + return 0;
3275 + }
3276 +
3277 + /* swap the 16-bit units (strings, table16, array16) */
3278 + if(keysTop<resBottom) {
3279 + ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBun dle+keysTop, pErrorCode);
3280 + if(U_FAILURE(*pErrorCode)) {
3281 + udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
3282 + return 0;
3283 + }
3284 + }
3285 +
3286 + /* allocate the temporary table for sorting resource tables */
3287 + tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
3288 + if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY ) {
3289 + tempTable.rows=rows;
3290 + tempTable.resort=resort;
3291 + } else {
3292 + tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTab leLength*4);
3293 + if(tempTable.rows==NULL) {
3294 + udata_printError(ds, "ures_swap(): unable to allocate memory fo r sorting tables (max length: %d)\n",
3295 + maxTableLength);
3296 + *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
3297 + if(tempTable.resFlags!=stackResFlags) {
3298 + uprv_free(tempTable.resFlags);
3299 + }
3300 + return 0;
3301 + }
3302 + tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
3303 + }
3304 +
3305 + /* swap the resources */
3306 + ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, p ErrorCode);
3307 + if(U_FAILURE(*pErrorCode)) {
3308 + udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
3309 + rootRes);
3310 + }
3311 +
3312 + if(tempTable.rows!=rows) {
3313 + uprv_free(tempTable.rows);
3314 + }
3315 + if(tempTable.resFlags!=stackResFlags) {
3316 + uprv_free(tempTable.resFlags);
3317 + }
3318 +
3319 + /* swap the root resource and indexes */
3320 + ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
3321 + }
3322 +
3323 + return headerSize+4*top;
3324 +}
3325 diff --git a/source/common/uresdata.h b/source/common/uresdata.h
3326 index ff69dd4..1afa77c 100644
3327 --- a/source/common/uresdata.h
3328 +++ b/source/common/uresdata.h
3329 @@ -453,11 +453,67 @@ res_getTableItemByIndex(const ResourceData *pResData, Reso urce table, int32_t in
3330 U_INTERNAL Resource U_EXPORT2
3331 res_getTableItemByKey(const ResourceData *pResData, Resource table, int32_t *in dexS, const char* * key);
3332
3333 -/*
3334 +/**
3335 + * Iterates over the path and stops when a scalar resource is found.
3336 + * Follows aliases.
3337 * Modifies the contents of *path (replacing separators with NULs),
3338 * and also moves *path forward while it finds items.
3339 + *
3340 + * @param path input: "CollationElements/Sequence" or "zoneStrings/3/2" etc.;
3341 + * output: points to the part that has not yet been processed
3342 + */
3343 +U_CFUNC Resource res_findResource(const ResourceData *pResData, Resource r,
3344 + char** path, const char** key);
3345 +
3346 +#ifdef __cplusplus
3347 +
3348 +#include "resource.h"
3349 +
3350 +U_NAMESPACE_BEGIN
3351 +
3352 +class ResourceDataValue : public ResourceValue {
3353 +public:
3354 + ResourceDataValue() : pResData(NULL), res(URES_NONE) {}
3355 + virtual ~ResourceDataValue();
3356 +
3357 + void setData(const ResourceData *data) { pResData = data; }
3358 + void setResource(Resource r) { res = r; }
3359 +
3360 + virtual UResType getType() const;
3361 + virtual const UChar *getString(int32_t &length, UErrorCode &errorCode) cons t;
3362 + virtual const UChar *getAliasString(int32_t &length, UErrorCode &errorCode) const;
3363 + virtual int32_t getInt(UErrorCode &errorCode) const;
3364 + virtual uint32_t getUInt(UErrorCode &errorCode) const;
3365 + virtual const int32_t *getIntVector(int32_t &length, UErrorCode &errorCode) const;
3366 + virtual const uint8_t *getBinary(int32_t &length, UErrorCode &errorCode) co nst;
3367 +
3368 + const ResourceData *pResData;
3369 +
3370 +private:
3371 + Resource res;
3372 +};
3373 +
3374 +U_NAMESPACE_END
3375 +
3376 +/**
3377 + * @param value will be set during enumeration; input contents is ignored
3378 + * @param sink receives all table item key-value pairs
3379 */
3380 -U_CFUNC Resource res_findResource(const ResourceData *pResData, Resource r, cha r** path, const char** key);
3381 +U_CFUNC void
3382 +ures_getAllTableItems(const ResourceData *pResData, Resource table,
3383 + icu::ResourceDataValue &value, icu::ResourceTableSink &si nk,
3384 + UErrorCode &errorCode);
3385 +
3386 +/**
3387 + * @param value will be set during enumeration; input contents is ignored
3388 + * @param sink receives all array item values
3389 + */
3390 +U_CFUNC void
3391 +ures_getAllArrayItems(const ResourceData *pResData, Resource array,
3392 + icu::ResourceDataValue &value, icu::ResourceArraySink &si nk,
3393 + UErrorCode &errorCode);
3394 +
3395 +#endif /* __cplusplus */
3396
3397 /**
3398 * Swap an ICU resource bundle. See udataswp.h.
3399 diff --git a/source/common/uresimp.h b/source/common/uresimp.h
3400 index b8ec5a6..6b264db 100644
3401 --- a/source/common/uresimp.h
3402 +++ b/source/common/uresimp.h
3403 @@ -1,6 +1,6 @@
3404 /*
3405 **********************************************************************
3406 -* Copyright (C) 2000-2014, International Business Machines
3407 +* Copyright (C) 2000-2015, International Business Machines
3408 * Corporation and others. All Rights Reserved.
3409 **********************************************************************
3410 */
3411 @@ -222,6 +222,18 @@ ures_getStringByKeyWithFallback(const UResourceBundle *resB ,
3412 int32_t* len,
3413 UErrorCode *status);
3414
3415 +#ifdef __cplusplus
3416 +
3417 +U_CAPI void U_EXPORT2
3418 +ures_getAllArrayItemsWithFallback(const UResourceBundle *bundle, const char *pa th,
3419 + icu::ResourceArraySink &sink, UErrorCode &err orCode);
3420 +
3421 +U_CAPI void U_EXPORT2
3422 +ures_getAllTableItemsWithFallback(const UResourceBundle *bundle, const char *pa th,
3423 + icu::ResourceTableSink &sink, UErrorCode &err orCode);
3424 +
3425 +#endif /* __cplusplus */
3426 +
3427 /**
3428 * Get a version number by key
3429 * @param resB bundle containing version number
3430 diff --git a/source/common/uresource.cpp b/source/common/uresource.cpp
3431 new file mode 100644
3432 index 0000000..e69de29
3433 diff --git a/source/common/uresource.h b/source/common/uresource.h
3434 new file mode 100644
3435 index 0000000..e69de29
3436 diff --git a/source/data/unit/ar.txt b/source/data/unit/ar.txt
3437 index 883e97c..d6a1d3f 100755
3438 --- a/source/data/unit/ar.txt
3439 +++ b/source/data/unit/ar.txt
3440 @@ -400,7 +400,7 @@ ar{
3441 many{"{0} سنة"}
3442 one{"سنة"}
3443 other{"{0} سنة"}
3444 - per{"في السنة"}
3445 + per{"{0} في السنة"}
3446 two{"سنتان"}
3447 zero{"{0} سنة"}
3448 }
3449 diff --git a/source/data/unit/de.txt b/source/data/unit/de.txt
3450 index 9ec6e5d..eab9dc1 100755
3451 --- a/source/data/unit/de.txt
3452 +++ b/source/data/unit/de.txt
3453 @@ -467,7 +467,7 @@ de{
3454 dnam{"Unzen"}
3455 one{"{0} Unze"}
3456 other{"{0} Unzen"}
3457 - per{"pro Unze"}
3458 + per{"{0} pro Unze"}
3459 }
3460 ounce-troy{
3461 dnam{"Feinunzen"}
3462 diff --git a/source/data/unit/dsb.txt b/source/data/unit/dsb.txt
3463 index fbdfc9f..d972a0d 100755
3464 --- a/source/data/unit/dsb.txt
3465 +++ b/source/data/unit/dsb.txt
3466 @@ -226,7 +226,7 @@ dsb{
3467 few{"{0} góźiny"}
3468 one{"{0} góźina"}
3469 other{"{0} góźinow"}
3470 - per{"na góźinu"}
3471 + per{"{0} na góźinu"}
3472 two{"{0} góźinje"}
3473 }
3474 microsecond{
3475 @@ -269,7 +269,7 @@ dsb{
3476 few{"{0} sekundy"}
3477 one{"{0} sekunda"}
3478 other{"{0} sekundow"}
3479 - per{"na sekundu"}
3480 + per{"{0} na sekundu"}
3481 two{"{0} sekunźe"}
3482 }
3483 week{
3484 diff --git a/source/data/unit/hsb.txt b/source/data/unit/hsb.txt
3485 index 747481e..bdf5112 100755
3486 --- a/source/data/unit/hsb.txt
3487 +++ b/source/data/unit/hsb.txt
3488 @@ -226,7 +226,7 @@ hsb{
3489 few{"{0} hodźiny"}
3490 one{"{0} hodźina"}
3491 other{"{0} hodźinow"}
3492 - per{"na hodźinu"}
3493 + per{"{0} na hodźinu"}
3494 two{"{0} hodźinje"}
3495 }
3496 microsecond{
3497 @@ -269,7 +269,7 @@ hsb{
3498 few{"{0} sekundy"}
3499 one{"{0} sekunda"}
3500 other{"{0} sekundow"}
3501 - per{"na sekundu"}
3502 + per{"{0} na sekundu"}
3503 two{"{0} sekundźe"}
3504 }
3505 week{
3506 diff --git a/source/data/unit/lv.txt b/source/data/unit/lv.txt
3507 index 81e8a23..1536fca 100755
3508 --- a/source/data/unit/lv.txt
3509 +++ b/source/data/unit/lv.txt
3510 @@ -77,7 +77,7 @@ lv{
3511 dnam{"kvadrātcentimetri"}
3512 one{"{0} kvadrātcentimetrs"}
3513 other{"{0} kvadrātcentimetri"}
3514 - per{"uz kvadrātcentimetru"}
3515 + per{"{0} uz kvadrātcentimetru"}
3516 zero{"{0} kvadrātcentimetri"}
3517 }
3518 square-foot{
3519 @@ -103,7 +103,7 @@ lv{
3520 dnam{"kvadrātmetri"}
3521 one{"{0} kvadrātmetrs"}
3522 other{"{0} kvadrātmetri"}
3523 - per{"uz kvadrātmetru"}
3524 + per{"{0} uz kvadrātmetru"}
3525 zero{"{0} kvadrātmetri"}
3526 }
3527 square-mile{
3528 @@ -704,7 +704,7 @@ lv{
3529 dnam{"kubikcentimetri"}
3530 one{"{0} kubikcentimetrs"}
3531 other{"{0} kubikcentimetri"}
3532 - per{"uz kubikcentimetru"}
3533 + per{"{0} uz kubikcentimetru"}
3534 zero{"{0} kubikcentimetri"}
3535 }
3536 cubic-foot{
3537 @@ -729,7 +729,7 @@ lv{
3538 dnam{"kubikmetri"}
3539 one{"{0} kubikmetrs"}
3540 other{"{0} kubikmetri"}
3541 - per{"uz kubikmetru"}
3542 + per{"{0} uz kubikmetru"}
3543 zero{"{0} kubikmetri"}
3544 }
3545 cubic-mile{
3546 @@ -779,7 +779,7 @@ lv{
3547 dnam{"litri"}
3548 one{"{0} litrs"}
3549 other{"{0} litri"}
3550 - per{"uz litru"}
3551 + per{"{0} uz litru"}
3552 zero{"{0} litri"}
3553 }
3554 megaliter{
3555 diff --git a/source/data/unit/my.txt b/source/data/unit/my.txt
3556 index 0a24ab4..07b5dcc 100755
3557 --- a/source/data/unit/my.txt
3558 +++ b/source/data/unit/my.txt
3559 @@ -386,7 +386,7 @@ my{
3560 pound{
3561 dnam{"ပေါင်"}
3562 other{"{0}ပေါင်"}
3563 - per{"တစ်ပေါင်လျှင်"}
3564 + per{"{0}တစ်ပေါင်လျှင်"}
3565 }
3566 ton{
3567 dnam{"တန်"}
3568 @@ -512,7 +512,7 @@ my{
3569 cubic-meter{
3570 dnam{"ကုဗမီတာ"}
3571 other{"{0}ကုဗမီတာ"}
3572 - per{"တစ်ကုဗစင်တီမီတာလျှင်"}
3573 + per{"{0}တစ်ကုဗမီတာလျှင်"}
3574 }
3575 cubic-mile{
3576 dnam{"ကုဗမိုင်"}
3577 diff --git a/source/data/unit/pt_PT.txt b/source/data/unit/pt_PT.txt
3578 index a3b82c7..3108255 100755
3579 --- a/source/data/unit/pt_PT.txt
3580 +++ b/source/data/unit/pt_PT.txt
3581 @@ -110,7 +110,7 @@ pt_PT{
3582 }
3583 volume{
3584 cubic-centimeter{
3585 - per{"por centímetro cúbico"}
3586 + per{"{0} por centímetro cúbico"}
3587 }
3588 cubic-kilometer{
3589 dnam{"quilómetros cúbicos"}
3590 diff --git a/source/data/unit/ro.txt b/source/data/unit/ro.txt
3591 index a31e161..e74eaa8 100755
3592 --- a/source/data/unit/ro.txt
3593 +++ b/source/data/unit/ro.txt
3594 @@ -504,7 +504,7 @@ ro{
3595 few{"{0} grame"}
3596 one{"{0} gram"}
3597 other{"{0} de grame"}
3598 - per{"per gram"}
3599 + per{"{0} per gram"}
3600 }
3601 kilogram{
3602 dnam{"kilograme"}
3603 diff --git a/source/data/unit/si.txt b/source/data/unit/si.txt
3604 index 1f4da6b..47fae31 100755
3605 --- a/source/data/unit/si.txt
3606 +++ b/source/data/unit/si.txt
3607 @@ -230,7 +230,7 @@ si{
3608 dnam{"තත්පර"}
3609 one{"තත්පර {0}"}
3610 other{"තත්පර {0}"}
3611 - per{"තත්පරයට"}
3612 + per{"තත්පරයට {0}"}
3613 }
3614 week{
3615 dnam{"සති"}
3616 diff --git a/source/data/unit/sr.txt b/source/data/unit/sr.txt
3617 index a00f8aa..2d09960 100755
3618 --- a/source/data/unit/sr.txt
3619 +++ b/source/data/unit/sr.txt
3620 @@ -772,7 +772,7 @@ sr{
3621 few{"{0} литра"}
3622 one{"{0} литар"}
3623 other{"{0} литара"}
3624 - per{"по литри"}
3625 + per{"{0} по литри"}
3626 }
3627 megaliter{
3628 dnam{"мегалитри"}
3629 diff --git a/source/data/unit/sr_Latn.txt b/source/data/unit/sr_Latn.txt
3630 index 82830ae..0fe7b9e 100755
3631 --- a/source/data/unit/sr_Latn.txt
3632 +++ b/source/data/unit/sr_Latn.txt
3633 @@ -773,7 +773,7 @@ sr_Latn{
3634 few{"{0} litra"}
3635 one{"{0} litar"}
3636 other{"{0} litara"}
3637 - per{"po litri"}
3638 + per{"{0} po litri"}
3639 }
3640 megaliter{
3641 dnam{"megalitri"}
3642 diff --git a/source/data/unit/tr.txt b/source/data/unit/tr.txt
3643 index 8f5c1f0..39411a9 100755
3644 --- a/source/data/unit/tr.txt
3645 +++ b/source/data/unit/tr.txt
3646 @@ -68,7 +68,7 @@ tr{
3647 dnam{"santimetrekare"}
3648 one{"{0} santimetrekare"}
3649 other{"{0} santimetrekare"}
3650 - per{"/santimetrekare"}
3651 + per{"{0}/santimetrekare"}
3652 }
3653 square-foot{
3654 dnam{"fit kare"}
3655 @@ -90,7 +90,7 @@ tr{
3656 dnam{"metrekare"}
3657 one{"{0} metrekare"}
3658 other{"{0} metrekare"}
3659 - per{"/metrekare"}
3660 + per{"{0}/metrekare"}
3661 }
3662 square-mile{
3663 dnam{"mil kare"}
3664 @@ -642,7 +642,7 @@ tr{
3665 dnam{"metreküp"}
3666 one{"{0} metreküp"}
3667 other{"{0} metreküp"}
3668 - per{"/metreküp"}
3669 + per{"{0}/metreküp"}
3670 }
3671 cubic-mile{
3672 dnam{"mil küp"}
3673 diff --git a/source/i18n/Makefile.in b/source/i18n/Makefile.in
3674 index 53dd5fd..b59b2ce 100644
3675 --- a/source/i18n/Makefile.in
3676 +++ b/source/i18n/Makefile.in
3677 @@ -88,7 +88,7 @@ regexcmp.o rematch.o repattrn.o regexst.o regextxt.o regeximp. o uregex.o uregexc
3678 ulocdata.o measfmt.o currfmt.o curramt.o currunit.o measure.o utmscale.o \
3679 csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8 .o inputext.o \
3680 wintzimpl.o windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o zonemeta.o \
3681 -upluralrules.o plurrule.o plurfmt.o selfmt.o dtitvfmt.o dtitvinf.o udateinterva lformat.o \
3682 +standardplural.o upluralrules.o plurrule.o plurfmt.o selfmt.o dtitvfmt.o dtitvi nf.o udateintervalformat.o \
3683 tmunit.o tmutamt.o tmutfmt.o currpinf.o \
3684 uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o smpdtfst.o \
3685 ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o ufieldpositer.o locdspnm.o \
3686 diff --git a/source/i18n/i18n.vcxproj b/source/i18n/i18n.vcxproj
3687 index 8c586a9..b036d95 100644
3688 --- a/source/i18n/i18n.vcxproj
3689 +++ b/source/i18n/i18n.vcxproj
3690 @@ -364,6 +364,7 @@
3691 <ClCompile Include="scriptset.cpp" />
3692 <ClCompile Include="smpdtfmt.cpp" />
3693 <ClCompile Include="smpdtfst.cpp" />
3694 + <ClCompile Include="standardplural.cpp" />
3695 <ClCompile Include="taiwncal.cpp" />
3696 <ClCompile Include="timezone.cpp" />
3697 <ClCompile Include="tmunit.cpp" />
3698 @@ -1278,6 +1279,7 @@
3699 <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\ include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
3700 </CustomBuild>
3701 <ClInclude Include="smpdtfst.h" />
3702 + <ClInclude Include="standardplural.h" />
3703 <ClInclude Include="taiwncal.h" />
3704 <CustomBuild Include="unicode\timezone.h">
3705 <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy " %(FullPath)" ..\..\include\unicode
3706 diff --git a/source/i18n/i18n.vcxproj.filters b/source/i18n/i18n.vcxproj.filters
3707 index 35c3fe2..9164f5a 100644
3708 --- a/source/i18n/i18n.vcxproj.filters
3709 +++ b/source/i18n/i18n.vcxproj.filters
3710 @@ -282,6 +282,9 @@
3711 <ClCompile Include="smpdtfst.cpp">
3712 <Filter>formatting</Filter>
3713 </ClCompile>
3714 + <ClCompile Include="standardplural.cpp">
3715 + <Filter>formatting</Filter>
3716 + </ClCompile>
3717 <ClCompile Include="taiwncal.cpp">
3718 <Filter>formatting</Filter>
3719 </ClCompile>
3720 @@ -787,6 +790,9 @@
3721 <ClInclude Include="smpdtfst.h">
3722 <Filter>formatting</Filter>
3723 </ClInclude>
3724 + <ClInclude Include="standardplural.h">
3725 + <Filter>formatting</Filter>
3726 + </ClInclude>
3727 <ClInclude Include="taiwncal.h">
3728 <Filter>formatting</Filter>
3729 </ClInclude>
3730 diff --git a/source/i18n/measfmt.cpp b/source/i18n/measfmt.cpp
3731 index 6f8851c..1af72e9 100644
3732 --- a/source/i18n/measfmt.cpp
3733 +++ b/source/i18n/measfmt.cpp
3734 @@ -17,6 +17,7 @@
3735 #include "unicode/numfmt.h"
3736 #include "currfmt.h"
3737 #include "unicode/localpointer.h"
3738 +#include "resource.h"
3739 #include "simplepatternformatter.h"
3740 #include "quantityformatter.h"
3741 #include "unicode/plurrule.h"
3742 @@ -35,6 +36,7 @@
3743
3744 #include "sharednumberformat.h"
3745 #include "sharedpluralrules.h"
3746 +#include "standardplural.h"
3747 #include "unifiedcache.h"
3748
3749 #define MEAS_UNIT_COUNT 129
3750 @@ -76,20 +78,53 @@ private:
3751 NumericDateFormatters &operator=(const NumericDateFormatters &other);
3752 };
3753
3754 -// Instances contain all MeasureFormat specific data for a particular locale.
3755 -// This data is cached. It is never copied, but is shared via shared pointers.
3756 +static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) {
3757 + if (width >= WIDTH_INDEX_COUNT) {
3758 + return UMEASFMT_WIDTH_NARROW;
3759 + }
3760 + return width;
3761 +}
3762 +
3763 +/**
3764 + * Instances contain all MeasureFormat specific data for a particular locale.
3765 + * This data is cached. It is never copied, but is shared via shared pointers.
3766 + *
3767 + * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
3768 + * complete sets of unit & per patterns,
3769 + * to correspond to the resource data and its aliases.
3770 + *
3771 + * TODO: Maybe store more sparsely in general, with pointers rather than potent ially-empty objects.
3772 + */
3773 class MeasureFormatCacheData : public SharedObject {
3774 public:
3775 - QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
3776 + static const int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
3777 + static const int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
3778 +
3779 + /**
3780 + * Redirection data from root-bundle, top-level sideways aliases.
3781 + * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
3782 + * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
3783 + */
3784 + UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
3785 + /** Measure unit -> format width -> array of patterns ("{0} meters") (plura ls + PER_UNIT_INDEX) */
3786 + SimplePatternFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTER N_COUNT];
3787 SimplePatternFormatter perFormatters[WIDTH_INDEX_COUNT];
3788
3789 MeasureFormatCacheData();
3790 + virtual ~MeasureFormatCacheData();
3791 +
3792 + UBool hasPerFormatter(int32_t width) const {
3793 + // TODO: Create a more obvious way to test if the per-formatter has bee n set?
3794 + // Use pointers, check for NULL? Or add an isValid() method?
3795 + return perFormatters[width].getPlaceholderCount() == 2;
3796 + }
3797 +
3798 void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
3799 delete currencyFormats[widthIndex];
3800 currencyFormats[widthIndex] = nfToAdopt;
3801 }
3802 - const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
3803 - return currencyFormats[widthIndex];
3804 + const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const {
3805 + return currencyFormats[getRegularWidth(width)];
3806 }
3807 void adoptIntegerFormat(NumberFormat *nfToAdopt) {
3808 delete integerFormat;
3809 @@ -105,36 +140,23 @@ public:
3810 const NumericDateFormatters *getNumericDateFormatters() const {
3811 return numericDateFormatters;
3812 }
3813 - void adoptPerUnitFormatter(
3814 - int32_t index,
3815 - int32_t widthIndex,
3816 - SimplePatternFormatter *formatterToAdopt) {
3817 - delete perUnitFormatters[index][widthIndex];
3818 - perUnitFormatters[index][widthIndex] = formatterToAdopt;
3819 - }
3820 - const SimplePatternFormatter * const * getPerUnitFormattersByIndex(
3821 - int32_t index) const {
3822 - return perUnitFormatters[index];
3823 - }
3824 - virtual ~MeasureFormatCacheData();
3825 +
3826 private:
3827 NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
3828 NumberFormat *integerFormat;
3829 NumericDateFormatters *numericDateFormatters;
3830 - SimplePatternFormatter *perUnitFormatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUN T];
3831 MeasureFormatCacheData(const MeasureFormatCacheData &other);
3832 MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
3833 };
3834
3835 MeasureFormatCacheData::MeasureFormatCacheData() {
3836 + for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
3837 + widthFallback[i] = UMEASFMT_WIDTH_COUNT;
3838 + }
3839 for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
3840 currencyFormats[i] = NULL;
3841 }
3842 - for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
3843 - for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
3844 - perUnitFormatters[i][j] = NULL;
3845 - }
3846 - }
3847 + uprv_memset(patterns, 0, sizeof(patterns));
3848 integerFormat = NULL;
3849 numericDateFormatters = NULL;
3850 }
3851 @@ -145,20 +167,15 @@ MeasureFormatCacheData::~MeasureFormatCacheData() {
3852 }
3853 for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
3854 for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
3855 - delete perUnitFormatters[i][j];
3856 + for (int32_t k = 0; k < PATTERN_COUNT; ++k) {
3857 + delete patterns[i][j][k];
3858 + }
3859 }
3860 }
3861 delete integerFormat;
3862 delete numericDateFormatters;
3863 }
3864
3865 -static int32_t widthToIndex(UMeasureFormatWidth width) {
3866 - if (width >= WIDTH_INDEX_COUNT) {
3867 - return WIDTH_INDEX_COUNT - 1;
3868 - }
3869 - return width;
3870 -}
3871 -
3872 static UBool isCurrency(const MeasureUnit &unit) {
3873 return (uprv_strcmp(unit.getType(), "currency") == 0);
3874 }
3875 @@ -176,118 +193,219 @@ static UBool getString(
3876 return TRUE;
3877 }
3878
3879 +namespace {
3880
3881 -static UBool loadMeasureUnitData(
3882 - const UResourceBundle *resource,
3883 - MeasureFormatCacheData &cacheData,
3884 - UErrorCode &status) {
3885 - if (U_FAILURE(status)) {
3886 - return FALSE;
3887 - }
3888 - static const char *widthPath[] = {"units", "unitsShort", "unitsNarrow"};
3889 - MeasureUnit *units = NULL;
3890 - int32_t unitCount = MeasureUnit::getAvailable(units, 0, status);
3891 - while (status == U_BUFFER_OVERFLOW_ERROR) {
3892 - status = U_ZERO_ERROR;
3893 - delete [] units;
3894 - units = new MeasureUnit[unitCount];
3895 - if (units == NULL) {
3896 - status = U_MEMORY_ALLOCATION_ERROR;
3897 - return FALSE;
3898 - }
3899 - unitCount = MeasureUnit::getAvailable(units, unitCount, status);
3900 - }
3901 - for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentW idth) {
3902 - // Be sure status is clear since next resource bundle lookup may fail.
3903 - if (U_FAILURE(status)) {
3904 - delete [] units;
3905 - return FALSE;
3906 - }
3907 - LocalUResourceBundlePointer widthBundle(
3908 - ures_getByKeyWithFallback(
3909 - resource, widthPath[currentWidth], NULL, &status));
3910 - // We may not have data for all widths in all locales.
3911 - if (status == U_MISSING_RESOURCE_ERROR) {
3912 - status = U_ZERO_ERROR;
3913 - continue;
3914 +static const UChar g_LOCALE_units[] = {
3915 + 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
3916 + 0x75, 0x6E, 0x69, 0x74, 0x73
3917 +};
3918 +static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
3919 +static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
3920 +
3921 +/**
3922 + * Sink for enumerating all of the measurement unit display names.
3923 + * Contains inner sink classes, each one corresponding to a type of resource ta ble.
3924 + * The outer sink handles the top-level units, unitsNarrow, and unitsShort tabl es.
3925 + *
3926 + * More specific bundles (en_GB) are enumerated before their parents (en_001, e n, root):
3927 + * Only store a value if it is still missing, that is, it has not been overridd en.
3928 + *
3929 + * C++: Each inner sink class has a reference to the main outer sink.
3930 + * Java: Use non-static inner classes instead.
3931 + */
3932 +struct UnitDataSink : public ResourceTableSink {
3933 + /**
3934 + * Sink for a table of display patterns. For example,
3935 + * unitsShort/duration/hour contains other{"{0} hrs"}.
3936 + */
3937 + struct UnitPatternSink : public ResourceTableSink {
3938 + UnitPatternSink(UnitDataSink &sink) : outer(sink) {}
3939 + ~UnitPatternSink();
3940 +
3941 + void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
3942 + int32_t minPlaceholders, UErrorCode &errorCod e) {
3943 + SimplePatternFormatter **patterns =
3944 + &outer.cacheData.patterns[outer.unitIndex][outer.width][0];
3945 + if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
3946 + patterns[index] = new SimplePatternFormatter(
3947 + value.getUnicodeString(errorCode), minPlaceholders, 1, e rrorCode);
3948 + if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
3949 + errorCode = U_MEMORY_ALLOCATION_ERROR;
3950 + }
3951 + }
3952 }
3953 - {
3954 - // compound per
3955 - LocalUResourceBundlePointer compoundPerBundle(
3956 - ures_getByKeyWithFallback(
3957 - widthBundle.getAlias(),
3958 - "compound/per",
3959 - NULL,
3960 - &status));
3961 - if (U_FAILURE(status)) {
3962 - status = U_ZERO_ERROR;
3963 +
3964 + virtual void put(const char *key, const ResourceValue &value, UErrorCod e &errorCode) {
3965 + if (U_FAILURE(errorCode)) { return; }
3966 + if (uprv_strcmp(key, "dnam") == 0) {
3967 + // Skip the unit display name for now.
3968 + } else if (uprv_strcmp(key, "per") == 0) {
3969 + // For example, "{0}/h".
3970 + setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, va lue, 1, errorCode);
3971 } else {
3972 - UnicodeString perPattern;
3973 - getString(compoundPerBundle.getAlias(), perPattern, status);
3974 - cacheData.perFormatters[currentWidth].compile(perPattern, statu s);
3975 + // The key must be one of the plural form strings. For example:
3976 + // one{"{0} hr"}
3977 + // other{"{0} hrs"}
3978 + setFormatterIfAbsent(StandardPlural::indexFromString(key, error Code), value, 0,
3979 + errorCode);
3980 }
3981 }
3982 - for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) {
3983 - // Be sure status is clear next lookup may fail.
3984 - if (U_FAILURE(status)) {
3985 - delete [] units;
3986 - return FALSE;
3987 + UnitDataSink &outer;
3988 + } patternSink;
3989 +
3990 + /**
3991 + * Sink for a table of per-unit tables. For example,
3992 + * unitsShort/duration contains tables for duration-unit subtypes day & hou r.
3993 + */
3994 + struct UnitSubtypeSink : public ResourceTableSink {
3995 + UnitSubtypeSink(UnitDataSink &sink) : outer(sink) {}
3996 + ~UnitSubtypeSink();
3997 + virtual ResourceTableSink *getOrCreateTableSink(
3998 + const char *key, int32_t /* initialSize */, UErrorCode &errorCo de) {
3999 + if (U_FAILURE(errorCode)) { return NULL; }
4000 + outer.unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(ou ter.type, key);
4001 + if (outer.unitIndex >= 0) {
4002 + return &outer.patternSink;
4003 }
4004 - if (isCurrency(units[currentUnit])) {
4005 - continue;
4006 + return NULL;
4007 + }
4008 + UnitDataSink &outer;
4009 + } subtypeSink;
4010 +
4011 + /**
4012 + * Sink for compound x-per-y display pattern. For example,
4013 + * unitsShort/compound/per may be "{0}/{1}".
4014 + */
4015 + struct UnitCompoundSink : public ResourceTableSink {
4016 + UnitCompoundSink(UnitDataSink &sink) : outer(sink) {}
4017 + ~UnitCompoundSink();
4018 + virtual void put(const char *key, const ResourceValue &value, UErrorCod e &errorCode) {
4019 + if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) {
4020 + outer.cacheData.perFormatters[outer.width].
4021 + compileMinMaxPlaceholders(value.getUnicodeString(errorC ode), 2, 2, errorCode);
4022 }
4023 - CharString pathBuffer;
4024 - pathBuffer.append(units[currentUnit].getType(), status)
4025 - .append("/", status)
4026 - .append(units[currentUnit].getSubtype(), status);
4027 - LocalUResourceBundlePointer unitBundle(
4028 - ures_getByKeyWithFallback(
4029 - widthBundle.getAlias(),
4030 - pathBuffer.data(),
4031 - NULL,
4032 - &status));
4033 - // We may not have data for all units in all widths
4034 - if (status == U_MISSING_RESOURCE_ERROR) {
4035 - status = U_ZERO_ERROR;
4036 - continue;
4037 + }
4038 + UnitDataSink &outer;
4039 + } compoundSink;
4040 +
4041 + /**
4042 + * Sink for a table of unit type tables. For example,
4043 + * unitsShort contains tables for area & duration.
4044 + * It also contains a table for the compound/per pattern.
4045 + */
4046 + struct UnitTypeSink : public ResourceTableSink {
4047 + UnitTypeSink(UnitDataSink &sink) : outer(sink) {}
4048 + ~UnitTypeSink();
4049 + virtual ResourceTableSink *getOrCreateTableSink(
4050 + const char *key, int32_t /* initialSize */, UErrorCode &errorCo de) {
4051 + if (U_FAILURE(errorCode)) { return NULL; }
4052 + if (uprv_strcmp(key, "currency") == 0) {
4053 + // Skip.
4054 + } else if (uprv_strcmp(key, "compound") == 0) {
4055 + if (!outer.cacheData.hasPerFormatter(outer.width)) {
4056 + return &outer.compoundSink;
4057 + }
4058 + } else {
4059 + outer.type = key;
4060 + return &outer.subtypeSink;
4061 }
4062 - // We must have the unit bundle to proceed
4063 - if (U_FAILURE(status)) {
4064 - delete [] units;
4065 - return FALSE;
4066 + return NULL;
4067 + }
4068 + UnitDataSink &outer;
4069 + } typeSink;
4070 +
4071 + UnitDataSink(MeasureFormatCacheData &outputData)
4072 + : patternSink(*this), subtypeSink(*this), compoundSink(*this), type Sink(*this),
4073 + cacheData(outputData),
4074 + width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
4075 + ~UnitDataSink();
4076 + virtual void put(const char *key, const ResourceValue &value, UErrorCode &e rrorCode) {
4077 + // Handle aliases like
4078 + // units:alias{"/LOCALE/unitsShort"}
4079 + // which should only occur in the root bundle.
4080 + if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS) { return; }
4081 + UMeasureFormatWidth sourceWidth = widthFromKey(key);
4082 + if (sourceWidth == UMEASFMT_WIDTH_COUNT) {
4083 + // Alias from something we don't care about.
4084 + return;
4085 + }
4086 + UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode);
4087 + if (targetWidth == UMEASFMT_WIDTH_COUNT) {
4088 + // We do not recognize what to fall back to.
4089 + errorCode = U_INVALID_FORMAT_ERROR;
4090 + return;
4091 + }
4092 + // Check that we do not fall back to another fallback.
4093 + if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) {
4094 + errorCode = U_INVALID_FORMAT_ERROR;
4095 + return;
4096 + }
4097 + cacheData.widthFallback[sourceWidth] = targetWidth;
4098 + }
4099 + virtual ResourceTableSink *getOrCreateTableSink(
4100 + const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
4101 + if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WID TH_COUNT) {
4102 + return &typeSink;
4103 + }
4104 + return NULL;
4105 + }
4106 +
4107 + static UMeasureFormatWidth widthFromKey(const char *key) {
4108 + if (uprv_strncmp(key, "units", 5) == 0) {
4109 + key += 5;
4110 + if (*key == 0) {
4111 + return UMEASFMT_WIDTH_WIDE;
4112 + } else if (uprv_strcmp(key, "Short") == 0) {
4113 + return UMEASFMT_WIDTH_SHORT;
4114 + } else if (uprv_strcmp(key, "Narrow") == 0) {
4115 + return UMEASFMT_WIDTH_NARROW;
4116 }
4117 - int32_t size = ures_getSize(unitBundle.getAlias());
4118 - for (int32_t plIndex = 0; plIndex < size; ++plIndex) {
4119 - LocalUResourceBundlePointer pluralBundle(
4120 - ures_getByIndex(
4121 - unitBundle.getAlias(), plIndex, NULL, &status)) ;
4122 - if (U_FAILURE(status)) {
4123 - delete [] units;
4124 - return FALSE;
4125 - }
4126 - const char * resKey = ures_getKey(pluralBundle.getAlias());
4127 - if (uprv_strcmp(resKey, "dnam") == 0) {
4128 - continue; // skip display name & per pattern (new in CLDR 2 6 / ICU 54) for now, not part of plurals
4129 - }
4130 - if (uprv_strcmp(resKey, "per") == 0) {
4131 - UnicodeString perPattern;
4132 - getString(pluralBundle.getAlias(), perPattern, status);
4133 - cacheData.adoptPerUnitFormatter(
4134 - units[currentUnit].getIndex(),
4135 - currentWidth,
4136 - new SimplePatternFormatter(perPattern));
4137 - continue;
4138 - }
4139 - UnicodeString rawPattern;
4140 - getString(pluralBundle.getAlias(), rawPattern, status);
4141 - cacheData.formatters[units[currentUnit].getIndex()][currentWidt h].add(
4142 - resKey,
4143 - rawPattern,
4144 - status);
4145 + }
4146 + return UMEASFMT_WIDTH_COUNT;
4147 + }
4148 +
4149 + static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErro rCode &errorCode) {
4150 + int32_t length;
4151 + const UChar *s = value.getAliasString(length, errorCode);
4152 + // For example: "/LOCALE/unitsShort"
4153 + if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) {
4154 + s += 13;
4155 + length -= 13;
4156 + if (*s == 0) {
4157 + return UMEASFMT_WIDTH_WIDE;
4158 + } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) {
4159 + return UMEASFMT_WIDTH_SHORT;
4160 + } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) {
4161 + return UMEASFMT_WIDTH_NARROW;
4162 }
4163 }
4164 + return UMEASFMT_WIDTH_COUNT;
4165 }
4166 - delete [] units;
4167 +
4168 + // Output data.
4169 + MeasureFormatCacheData &cacheData;
4170 +
4171 + // Path to current data.
4172 + UMeasureFormatWidth width;
4173 + const char *type;
4174 + int32_t unitIndex;
4175 +};
4176 +
4177 +// Virtual destructors must be defined out of line.
4178 +UnitDataSink::UnitPatternSink::~UnitPatternSink() {}
4179 +UnitDataSink::UnitSubtypeSink::~UnitSubtypeSink() {}
4180 +UnitDataSink::UnitCompoundSink::~UnitCompoundSink() {}
4181 +UnitDataSink::UnitTypeSink::~UnitTypeSink() {}
4182 +UnitDataSink::~UnitDataSink() {}
4183 +
4184 +} // namespace
4185 +
4186 +static UBool loadMeasureUnitData(
4187 + const UResourceBundle *resource,
4188 + MeasureFormatCacheData &cacheData,
4189 + UErrorCode &status) {
4190 + UnitDataSink sink(cacheData);
4191 + ures_getAllTableItemsWithFallback(resource, "", sink, status);
4192 return U_SUCCESS(status);
4193 }
4194
4195 @@ -486,7 +604,9 @@ MeasureFormat::MeasureFormat(const MeasureFormat &other) :
4196 cache->addRef();
4197 numberFormat->addRef();
4198 pluralRules->addRef();
4199 - listFormatter = new ListFormatter(*other.listFormatter);
4200 + if (other.listFormatter != NULL) {
4201 + listFormatter = new ListFormatter(*other.listFormatter);
4202 + }
4203 }
4204
4205 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
4206 @@ -499,7 +619,11 @@ MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
4207 SharedObject::copyPtr(other.pluralRules, pluralRules);
4208 width = other.width;
4209 delete listFormatter;
4210 - listFormatter = new ListFormatter(*other.listFormatter);
4211 + if (other.listFormatter != NULL) {
4212 + listFormatter = new ListFormatter(*other.listFormatter);
4213 + } else {
4214 + listFormatter = NULL;
4215 + }
4216 return *this;
4217 }
4218
4219 @@ -714,7 +838,7 @@ void MeasureFormat::initMeasureFormat(
4220 delete listFormatter;
4221 listFormatter = ListFormatter::createInstance(
4222 locale,
4223 - listStyles[widthToIndex(width)],
4224 + listStyles[getRegularWidth(width)],
4225 status);
4226 }
4227
4228 @@ -771,24 +895,17 @@ UnicodeString &MeasureFormat::formatMeasure(
4229 if (isCurrency(amtUnit)) {
4230 UChar isoCode[4];
4231 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
4232 - return cache->getCurrencyFormat(widthToIndex(width))->format(
4233 + return cache->getCurrencyFormat(width)->format(
4234 new CurrencyAmount(amtNumber, isoCode, status),
4235 appendTo,
4236 pos,
4237 status);
4238 }
4239 - const QuantityFormatter *quantityFormatter = getQuantityFormatter(
4240 - amtUnit.getIndex(), widthToIndex(width), status);
4241 - if (U_FAILURE(status)) {
4242 - return appendTo;
4243 - }
4244 - return quantityFormatter->format(
4245 - amtNumber,
4246 - nf,
4247 - **pluralRules,
4248 - appendTo,
4249 - pos,
4250 - status);
4251 + UnicodeString formattedNumber;
4252 + StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
4253 + amtNumber, nf, **pluralRules, formattedNumber, pos, status);
4254 + const SimplePatternFormatter *formatter = getPluralFormatter(amtUnit, width , pluralForm, status);
4255 + return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos , status);
4256 }
4257
4258 // Formats hours-minutes-seconds as 5:37:23 or similar.
4259 @@ -920,64 +1037,69 @@ UnicodeString &MeasureFormat::formatNumeric(
4260 return appendTo;
4261 }
4262
4263 -const QuantityFormatter *MeasureFormat::getQuantityFormatter(
4264 - int32_t index,
4265 - int32_t widthIndex,
4266 - UErrorCode &status) const {
4267 - if (U_FAILURE(status)) {
4268 - return NULL;
4269 +const SimplePatternFormatter *MeasureFormat::getFormatterOrNull(
4270 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) cons t {
4271 + width = getRegularWidth(width);
4272 + SimplePatternFormatter *const (*unitPatterns)[MeasureFormatCacheData::PATTE RN_COUNT] =
4273 + &cache->patterns[unit.getIndex()][0];
4274 + if (unitPatterns[width][index] != NULL) {
4275 + return unitPatterns[width][index];
4276 }
4277 - const QuantityFormatter *formatters =
4278 - cache->formatters[index];
4279 - if (formatters[widthIndex].isValid()) {
4280 - return &formatters[widthIndex];
4281 + int32_t fallbackWidth = cache->widthFallback[width];
4282 + if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][in dex] != NULL) {
4283 + return unitPatterns[fallbackWidth][index];
4284 }
4285 - if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) {
4286 - return &formatters[UMEASFMT_WIDTH_SHORT];
4287 - }
4288 - status = U_MISSING_RESOURCE_ERROR;
4289 return NULL;
4290 }
4291
4292 -const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter(
4293 - int32_t index,
4294 - int32_t widthIndex) const {
4295 - const SimplePatternFormatter * const * perUnitFormatters =
4296 - cache->getPerUnitFormattersByIndex(index);
4297 - if (perUnitFormatters[widthIndex] != NULL) {
4298 - return perUnitFormatters[widthIndex];
4299 +const SimplePatternFormatter *MeasureFormat::getFormatter(
4300 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
4301 + UErrorCode &errorCode) const {
4302 + if (U_FAILURE(errorCode)) {
4303 + return NULL;
4304 }
4305 - if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) {
4306 - return perUnitFormatters[UMEASFMT_WIDTH_SHORT];
4307 + const SimplePatternFormatter *pattern = getFormatterOrNull(unit, width, ind ex);
4308 + if (pattern == NULL) {
4309 + errorCode = U_MISSING_RESOURCE_ERROR;
4310 }
4311 - return NULL;
4312 + return pattern;
4313 +}
4314 +
4315 +const SimplePatternFormatter *MeasureFormat::getPluralFormatter(
4316 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
4317 + UErrorCode &errorCode) const {
4318 + if (U_FAILURE(errorCode)) {
4319 + return NULL;
4320 + }
4321 + if (index != StandardPlural::OTHER) {
4322 + const SimplePatternFormatter *pattern = getFormatterOrNull(unit, width, index);
4323 + if (pattern != NULL) {
4324 + return pattern;
4325 + }
4326 + }
4327 + return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
4328 }
4329
4330 const SimplePatternFormatter *MeasureFormat::getPerFormatter(
4331 - int32_t widthIndex,
4332 + UMeasureFormatWidth width,
4333 UErrorCode &status) const {
4334 if (U_FAILURE(status)) {
4335 return NULL;
4336 }
4337 + width = getRegularWidth(width);
4338 const SimplePatternFormatter * perFormatters = cache->perFormatters;
4339 -
4340 - if (perFormatters[widthIndex].getPlaceholderCount() == 2) {
4341 - return &perFormatters[widthIndex];
4342 + if (perFormatters[width].getPlaceholderCount() == 2) {
4343 + return &perFormatters[width];
4344 }
4345 - if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) {
4346 - return &perFormatters[UMEASFMT_WIDTH_SHORT];
4347 + int32_t fallbackWidth = cache->widthFallback[width];
4348 + if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
4349 + perFormatters[fallbackWidth].getPlaceholderCount() == 2) {
4350 + return &perFormatters[fallbackWidth];
4351 }
4352 status = U_MISSING_RESOURCE_ERROR;
4353 return NULL;
4354 }
4355
4356 -static void getPerUnitString(
4357 - const QuantityFormatter &formatter,
4358 - UnicodeString &result) {
4359 - result = formatter.getByVariant("one")->getPatternWithNoPlaceholders();
4360 - result.trim();
4361 -}
4362 -
4363 int32_t MeasureFormat::withPerUnitAndAppend(
4364 const UnicodeString &formatted,
4365 const MeasureUnit &perUnit,
4366 @@ -987,8 +1109,8 @@ int32_t MeasureFormat::withPerUnitAndAppend(
4367 if (U_FAILURE(status)) {
4368 return offset;
4369 }
4370 - const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter(
4371 - perUnit.getIndex(), widthToIndex(width));
4372 + const SimplePatternFormatter *perUnitFormatter =
4373 + getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT _INDEX);
4374 if (perUnitFormatter != NULL) {
4375 const UnicodeString *params[] = {&formatted};
4376 perUnitFormatter->formatAndAppend(
4377 @@ -1000,15 +1122,14 @@ int32_t MeasureFormat::withPerUnitAndAppend(
4378 status);
4379 return offset;
4380 }
4381 - const SimplePatternFormatter *perFormatter = getPerFormatter(
4382 - widthToIndex(width), status);
4383 - const QuantityFormatter *qf = getQuantityFormatter(
4384 - perUnit.getIndex(), widthToIndex(width), status);
4385 + const SimplePatternFormatter *perFormatter = getPerFormatter(width, status) ;
4386 + const SimplePatternFormatter *pattern =
4387 + getPluralFormatter(perUnit, width, StandardPlural::ONE, status);
4388 if (U_FAILURE(status)) {
4389 return offset;
4390 }
4391 - UnicodeString perUnitString;
4392 - getPerUnitString(*qf, perUnitString);
4393 + UnicodeString perUnitString = pattern->getPatternWithNoPlaceholders();
4394 + perUnitString.trim();
4395 const UnicodeString *params[] = {&formatted, &perUnitString};
4396 perFormatter->formatAndAppend(
4397 params,
4398 diff --git a/source/i18n/measunit.cpp b/source/i18n/measunit.cpp
4399 index 35a56df..40b9547 100644
4400 --- a/source/i18n/measunit.cpp
4401 +++ b/source/i18n/measunit.cpp
4402 @@ -1152,6 +1152,18 @@ int32_t MeasureUnit::getIndexCount() {
4403 return gIndexes[UPRV_LENGTHOF(gIndexes) - 1];
4404 }
4405
4406 +int32_t MeasureUnit::internalGetIndexForTypeAndSubtype(const char *type, const char *subtype) {
4407 + int32_t t = binarySearch(gTypes, 0, UPRV_LENGTHOF(gTypes), type);
4408 + if (t < 0) {
4409 + return t;
4410 + }
4411 + int32_t st = binarySearch(gSubTypes, gOffsets[t], gOffsets[t + 1], subtype) ;
4412 + if (st < 0) {
4413 + return st;
4414 + }
4415 + return gIndexes[t] + st - gOffsets[t];
4416 +}
4417 +
4418 MeasureUnit *MeasureUnit::resolveUnitPerUnit(
4419 const MeasureUnit &unit, const MeasureUnit &perUnit) {
4420 int32_t unitOffset = unit.getOffset();
4421 diff --git a/source/i18n/plurrule_impl.h b/source/i18n/plurrule_impl.h
4422 index 7416fa7..785600b 100644
4423 --- a/source/i18n/plurrule_impl.h
4424 +++ b/source/i18n/plurrule_impl.h
4425 @@ -10,18 +10,19 @@
4426 */
4427
4428
4429 -#ifndef PLURRULE_IMPLE
4430 -#define PLURRULE_IMPLE
4431 +#ifndef PLURRULE_IMPL
4432 +#define PLURRULE_IMPL
4433
4434 // Internal definitions for the PluralRules implementation.
4435
4436 +#include "unicode/utypes.h"
4437 +
4438 #if !UCONFIG_NO_FORMATTING
4439
4440 #include "unicode/format.h"
4441 #include "unicode/locid.h"
4442 #include "unicode/parseerr.h"
4443 #include "unicode/ures.h"
4444 -#include "unicode/utypes.h"
4445 #include "uvector.h"
4446 #include "hash.h"
4447
4448 diff --git a/source/i18n/quantityformatter.cpp b/source/i18n/quantityformatter.c pp
4449 index ed80b38..97c4c80 100644
4450 --- a/source/i18n/quantityformatter.cpp
4451 +++ b/source/i18n/quantityformatter.cpp
4452 @@ -5,6 +5,11 @@
4453 ******************************************************************************
4454 * quantityformatter.cpp
4455 */
4456 +
4457 +#include "unicode/utypes.h"
4458 +
4459 +#if !UCONFIG_NO_FORMATTING
4460 +
4461 #include "quantityformatter.h"
4462 #include "simplepatternformatter.h"
4463 #include "uassert.h"
4464 @@ -15,26 +20,12 @@
4465 #include "charstr.h"
4466 #include "unicode/fmtable.h"
4467 #include "unicode/fieldpos.h"
4468 +#include "standardplural.h"
4469 #include "visibledigits.h"
4470 -
4471 -#if !UCONFIG_NO_FORMATTING
4472 +#include "uassert.h"
4473
4474 U_NAMESPACE_BEGIN
4475
4476 -// other must always be first.
4477 -static const char * const gPluralForms[] = {
4478 - "other", "zero", "one", "two", "few", "many"};
4479 -
4480 -static int32_t getPluralIndex(const char *pluralForm) {
4481 - int32_t len = UPRV_LENGTHOF(gPluralForms);
4482 - for (int32_t i = 0; i < len; ++i) {
4483 - if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
4484 - return i;
4485 - }
4486 - }
4487 - return -1;
4488 -}
4489 -
4490 QuantityFormatter::QuantityFormatter() {
4491 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
4492 formatters[i] = NULL;
4493 @@ -80,112 +71,126 @@ void QuantityFormatter::reset() {
4494 }
4495 }
4496
4497 -UBool QuantityFormatter::add(
4498 +UBool QuantityFormatter::addIfAbsent(
4499 const char *variant,
4500 const UnicodeString &rawPattern,
4501 UErrorCode &status) {
4502 + int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
4503 if (U_FAILURE(status)) {
4504 return FALSE;
4505 }
4506 - int32_t pluralIndex = getPluralIndex(variant);
4507 - if (pluralIndex == -1) {
4508 - status = U_ILLEGAL_ARGUMENT_ERROR;
4509 - return FALSE;
4510 + if (formatters[pluralIndex] != NULL) {
4511 + return TRUE;
4512 }
4513 - SimplePatternFormatter *newFmt =
4514 - new SimplePatternFormatter(rawPattern);
4515 + SimplePatternFormatter *newFmt = new SimplePatternFormatter(rawPattern, 0, 1, status);
4516 if (newFmt == NULL) {
4517 status = U_MEMORY_ALLOCATION_ERROR;
4518 return FALSE;
4519 }
4520 - if (newFmt->getPlaceholderCount() > 1) {
4521 + if (U_FAILURE(status)) {
4522 delete newFmt;
4523 - status = U_ILLEGAL_ARGUMENT_ERROR;
4524 return FALSE;
4525 }
4526 - delete formatters[pluralIndex];
4527 formatters[pluralIndex] = newFmt;
4528 return TRUE;
4529 }
4530
4531 UBool QuantityFormatter::isValid() const {
4532 - return formatters[0] != NULL;
4533 + return formatters[StandardPlural::OTHER] != NULL;
4534 }
4535
4536 const SimplePatternFormatter *QuantityFormatter::getByVariant(
4537 const char *variant) const {
4538 - int32_t pluralIndex = getPluralIndex(variant);
4539 - if (pluralIndex == -1) {
4540 - pluralIndex = 0;
4541 - }
4542 + U_ASSERT(isValid());
4543 + int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
4544 const SimplePatternFormatter *pattern = formatters[pluralIndex];
4545 if (pattern == NULL) {
4546 - pattern = formatters[0];
4547 + pattern = formatters[StandardPlural::OTHER];
4548 }
4549 return pattern;
4550 }
4551
4552 UnicodeString &QuantityFormatter::format(
4553 - const Formattable& quantity,
4554 + const Formattable &number,
4555 const NumberFormat &fmt,
4556 const PluralRules &rules,
4557 UnicodeString &appendTo,
4558 FieldPosition &pos,
4559 UErrorCode &status) const {
4560 + UnicodeString formattedNumber;
4561 + StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
4562 if (U_FAILURE(status)) {
4563 return appendTo;
4564 }
4565 - UnicodeString count;
4566 + const SimplePatternFormatter *pattern = formatters[p];
4567 + if (pattern == NULL) {
4568 + pattern = formatters[StandardPlural::OTHER];
4569 + if (pattern == NULL) {
4570 + status = U_INVALID_STATE_ERROR;
4571 + return appendTo;
4572 + }
4573 + }
4574 + return format(*pattern, formattedNumber, appendTo, pos, status);
4575 +}
4576 +
4577 +// The following methods live here so that class PluralRules does not depend on number formatting,
4578 +// and the SimplePatternFormatter does not depend on FieldPosition.
4579 +
4580 +StandardPlural::Form QuantityFormatter::selectPlural(
4581 + const Formattable &number,
4582 + const NumberFormat &fmt,
4583 + const PluralRules &rules,
4584 + UnicodeString &formattedNumber,
4585 + FieldPosition &pos,
4586 + UErrorCode &status) {
4587 + if (U_FAILURE(status)) {
4588 + return StandardPlural::OTHER;
4589 + }
4590 + UnicodeString pluralKeyword;
4591 VisibleDigitsWithExponent digits;
4592 const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
4593 if (decFmt != NULL) {
4594 - decFmt->initVisibleDigitsWithExponent(quantity, digits, status);
4595 + decFmt->initVisibleDigitsWithExponent(number, digits, status);
4596 if (U_FAILURE(status)) {
4597 - return appendTo;
4598 + return StandardPlural::OTHER;
4599 }
4600 - count = rules.select(digits);
4601 + pluralKeyword = rules.select(digits);
4602 + decFmt->format(digits, formattedNumber, pos, status);
4603 } else {
4604 - if (quantity.getType() == Formattable::kDouble) {
4605 - count = rules.select(quantity.getDouble());
4606 - } else if (quantity.getType() == Formattable::kLong) {
4607 - count = rules.select(quantity.getLong());
4608 - } else if (quantity.getType() == Formattable::kInt64) {
4609 - count = rules.select((double) quantity.getInt64());
4610 + if (number.getType() == Formattable::kDouble) {
4611 + pluralKeyword = rules.select(number.getDouble());
4612 + } else if (number.getType() == Formattable::kLong) {
4613 + pluralKeyword = rules.select(number.getLong());
4614 + } else if (number.getType() == Formattable::kInt64) {
4615 + pluralKeyword = rules.select((double) number.getInt64());
4616 } else {
4617 status = U_ILLEGAL_ARGUMENT_ERROR;
4618 - return appendTo;
4619 + return StandardPlural::OTHER;
4620 }
4621 + fmt.format(number, formattedNumber, pos, status);
4622 }
4623 - CharString buffer;
4624 - buffer.appendInvariantChars(count, status);
4625 + return StandardPlural::orOtherFromString(pluralKeyword);
4626 +}
4627 +
4628 +UnicodeString &QuantityFormatter::format(
4629 + const SimplePatternFormatter &pattern,
4630 + const UnicodeString &value,
4631 + UnicodeString &appendTo,
4632 + FieldPosition &pos,
4633 + UErrorCode &status) {
4634 if (U_FAILURE(status)) {
4635 return appendTo;
4636 }
4637 - const SimplePatternFormatter *pattern = getByVariant(buffer.data());
4638 - if (pattern == NULL) {
4639 - status = U_INVALID_STATE_ERROR;
4640 - return appendTo;
4641 - }
4642 - UnicodeString formattedNumber;
4643 - FieldPosition fpos(pos.getField());
4644 - if (decFmt != NULL) {
4645 - decFmt->format(digits, formattedNumber, fpos, status);
4646 - } else {
4647 - fmt.format(quantity, formattedNumber, fpos, status);
4648 - }
4649 - const UnicodeString *params[1] = {&formattedNumber};
4650 - int32_t offsets[1];
4651 - pattern->formatAndAppend(
4652 - params,
4653 - UPRV_LENGTHOF(params),
4654 - appendTo,
4655 - offsets,
4656 - UPRV_LENGTHOF(offsets),
4657 - status);
4658 - if (offsets[0] != -1) {
4659 - if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
4660 - pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);
4661 - pos.setEndIndex(fpos.getEndIndex() + offsets[0]);
4662 + const UnicodeString *param = &value;
4663 + int32_t offset;
4664 + pattern.formatAndAppend(&param, 1, appendTo, &offset, 1, status);
4665 + if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
4666 + if (offset >= 0) {
4667 + pos.setBeginIndex(pos.getBeginIndex() + offset);
4668 + pos.setEndIndex(pos.getEndIndex() + offset);
4669 + } else {
4670 + pos.setBeginIndex(0);
4671 + pos.setEndIndex(0);
4672 }
4673 }
4674 return appendTo;
4675 diff --git a/source/i18n/quantityformatter.h b/source/i18n/quantityformatter.h
4676 index 4e19085..6052d33 100644
4677 --- a/source/i18n/quantityformatter.h
4678 +++ b/source/i18n/quantityformatter.h
4679 @@ -1,6 +1,6 @@
4680 /*
4681 ******************************************************************************
4682 -* Copyright (C) 2014, International Business Machines
4683 +* Copyright (C) 2014-2015, International Business Machines
4684 * Corporation and others. All Rights Reserved.
4685 ******************************************************************************
4686 * quantityformatter.h
4687 @@ -14,6 +14,8 @@
4688
4689 #if !UCONFIG_NO_FORMATTING
4690
4691 +#include "standardplural.h"
4692 +
4693 U_NAMESPACE_BEGIN
4694
4695 class SimplePatternFormatter;
4696 @@ -64,17 +66,14 @@ public:
4697 void reset();
4698
4699 /**
4700 - * Adds a plural variant.
4701 - *
4702 - * @param variant "zero", "one", "two", "few", "many", "other"
4703 - * @param rawPattern the pattern for the variant e.g "{0} meters"
4704 - * @param status any error returned here.
4705 - * @return TRUE on success; FALSE if status was set to a non zero error.
4706 - */
4707 - UBool add(
4708 - const char *variant,
4709 - const UnicodeString &rawPattern,
4710 - UErrorCode &status);
4711 + * Adds a plural variant if there is none yet for the plural form.
4712 + *
4713 + * @param variant "zero", "one", "two", "few", "many", "other"
4714 + * @param rawPattern the pattern for the variant e.g "{0} meters"
4715 + * @param status any error returned here.
4716 + * @return TRUE on success; FALSE if status was set to a non zero error.
4717 + */
4718 + UBool addIfAbsent(const char *variant, const UnicodeString &rawPattern, UEr rorCode &status);
4719
4720 /**
4721 * returns TRUE if this object has at least the "other" variant.
4722 @@ -89,27 +88,48 @@ public:
4723 const SimplePatternFormatter *getByVariant(const char *variant) const;
4724
4725 /**
4726 - * Formats a quantity with this object appending the result to appendTo.
4727 + * Formats a number with this object appending the result to appendTo.
4728 * At least the "other" variant must be added to this object for this
4729 * method to work.
4730 *
4731 - * @param quantity the single quantity.
4732 - * @param fmt formats the quantity
4733 + * @param number the single number.
4734 + * @param fmt formats the number
4735 * @param rules computes the plural variant to use.
4736 * @param appendTo result appended here.
4737 * @param status any error returned here.
4738 * @return appendTo
4739 */
4740 UnicodeString &format(
4741 - const Formattable &quantity,
4742 + const Formattable &number,
4743 const NumberFormat &fmt,
4744 const PluralRules &rules,
4745 UnicodeString &appendTo,
4746 FieldPosition &pos,
4747 UErrorCode &status) const;
4748
4749 + /**
4750 + * Selects the standard plural form for the number/formatter/rules.
4751 + */
4752 + static StandardPlural::Form selectPlural(
4753 + const Formattable &number,
4754 + const NumberFormat &fmt,
4755 + const PluralRules &rules,
4756 + UnicodeString &formattedNumber,
4757 + FieldPosition &pos,
4758 + UErrorCode &status);
4759 +
4760 + /**
4761 + * Formats the pattern with the value and adjusts the FieldPosition.
4762 + */
4763 + static UnicodeString &format(
4764 + const SimplePatternFormatter &pattern,
4765 + const UnicodeString &value,
4766 + UnicodeString &appendTo,
4767 + FieldPosition &pos,
4768 + UErrorCode &status);
4769 +
4770 private:
4771 - SimplePatternFormatter *formatters[6];
4772 + SimplePatternFormatter *formatters[StandardPlural::COUNT];
4773 };
4774
4775 U_NAMESPACE_END
4776 diff --git a/source/i18n/reldatefmt.cpp b/source/i18n/reldatefmt.cpp
4777 index b4bfe42..4d8ca06 100644
4778 --- a/source/i18n/reldatefmt.cpp
4779 +++ b/source/i18n/reldatefmt.cpp
4780 @@ -1,10 +1,10 @@
4781 /*
4782 ******************************************************************************
4783 -* Copyright (C) 2014, International Business Machines Corporation and
4784 -* others. All Rights Reserved.
4785 +* Copyright (C) 2014-2015, International Business Machines Corporation and
4786 +* others. All Rights Reserved.
4787 ******************************************************************************
4788 -*
4789 -* File RELDATEFMT.CPP
4790 +*
4791 +* File reldatefmt.cpp
4792 ******************************************************************************
4793 */
4794
4795 @@ -186,7 +186,7 @@ static void initQuantityFormatter(
4796 if (!getString(pluralBundle.getAlias(), rawPattern, status)) {
4797 return;
4798 }
4799 - if (!formatter.add(
4800 + if (!formatter.addIfAbsent(
4801 ures_getKey(pluralBundle.getAlias()),
4802 rawPattern,
4803 status)) {
4804 diff --git a/source/i18n/standardplural.cpp b/source/i18n/standardplural.cpp
4805 new file mode 100644
4806 index 0000000..456e939
4807 --- /dev/null
4808 +++ b/source/i18n/standardplural.cpp
4809 @@ -0,0 +1,127 @@
4810 +/*
4811 + ****************************************************************************** *
4812 + * Copyright (C) 2015, International Business Machines Corporation
4813 + * and others. All Rights Reserved.
4814 + ****************************************************************************** *
4815 + * standardplural.cpp
4816 + *
4817 + * created on: 2015dec14
4818 + * created by: Markus W. Scherer
4819 + */
4820 +
4821 +#include "unicode/utypes.h"
4822 +
4823 +#if !UCONFIG_NO_FORMATTING
4824 +
4825 +#include "unicode/unistr.h"
4826 +#include "cstring.h"
4827 +#include "standardplural.h"
4828 +#include "uassert.h"
4829 +
4830 +U_NAMESPACE_BEGIN
4831 +
4832 +static const char *gKeywords[StandardPlural::COUNT] = {
4833 + "zero", "one", "two", "few", "many", "other"
4834 +};
4835 +
4836 +const char *StandardPlural::getKeyword(Form p) {
4837 + U_ASSERT(ZERO <= p && p < COUNT);
4838 + return gKeywords[p];
4839 +}
4840 +
4841 +int32_t StandardPlural::indexOrNegativeFromString(const char *keyword) {
4842 + switch (*keyword++) {
4843 + case 'f':
4844 + if (uprv_strcmp(keyword, "ew") == 0) {
4845 + return FEW;
4846 + }
4847 + break;
4848 + case 'm':
4849 + if (uprv_strcmp(keyword, "any") == 0) {
4850 + return MANY;
4851 + }
4852 + break;
4853 + case 'o':
4854 + if (uprv_strcmp(keyword, "ther") == 0) {
4855 + return OTHER;
4856 + } else if (uprv_strcmp(keyword, "ne") == 0) {
4857 + return ONE;
4858 + }
4859 + break;
4860 + case 't':
4861 + if (uprv_strcmp(keyword, "wo") == 0) {
4862 + return TWO;
4863 + }
4864 + break;
4865 + case 'z':
4866 + if (uprv_strcmp(keyword, "ero") == 0) {
4867 + return ZERO;
4868 + }
4869 + break;
4870 + default:
4871 + break;
4872 + }
4873 + return -1;
4874 +}
4875 +
4876 +static const UChar gZero[] = { 0x7A, 0x65, 0x72, 0x6F };
4877 +static const UChar gOne[] = { 0x6F, 0x6E, 0x65 };
4878 +static const UChar gTwo[] = { 0x74, 0x77, 0x6F };
4879 +static const UChar gFew[] = { 0x66, 0x65, 0x77 };
4880 +static const UChar gMany[] = { 0x6D, 0x61, 0x6E, 0x79 };
4881 +static const UChar gOther[] = { 0x6F, 0x74, 0x68, 0x65, 0x72 };
4882 +
4883 +int32_t StandardPlural::indexOrNegativeFromString(const UnicodeString &keyword) {
4884 + switch (keyword.length()) {
4885 + case 3:
4886 + if (keyword.compare(gOne, 3) == 0) {
4887 + return ONE;
4888 + } else if (keyword.compare(gTwo, 3) == 0) {
4889 + return TWO;
4890 + } else if (keyword.compare(gFew, 3) == 0) {
4891 + return FEW;
4892 + }
4893 + break;
4894 + case 4:
4895 + if (keyword.compare(gMany, 4) == 0) {
4896 + return MANY;
4897 + } else if (keyword.compare(gZero, 4) == 0) {
4898 + return ZERO;
4899 + }
4900 + break;
4901 + case 5:
4902 + if (keyword.compare(gOther, 5) == 0) {
4903 + return OTHER;
4904 + }
4905 + break;
4906 + default:
4907 + break;
4908 + }
4909 + return -1;
4910 +}
4911 +
4912 +int32_t StandardPlural::indexFromString(const char *keyword, UErrorCode &errorC ode) {
4913 + if (U_FAILURE(errorCode)) { return OTHER; }
4914 + int32_t i = indexOrNegativeFromString(keyword);
4915 + if (i >= 0) {
4916 + return i;
4917 + } else {
4918 + errorCode = U_ILLEGAL_ARGUMENT_ERROR;
4919 + return OTHER;
4920 + }
4921 +}
4922 +
4923 +int32_t StandardPlural::indexFromString(const UnicodeString &keyword, UErrorCod e &errorCode) {
4924 + if (U_FAILURE(errorCode)) { return OTHER; }
4925 + int32_t i = indexOrNegativeFromString(keyword);
4926 + if (i >= 0) {
4927 + return i;
4928 + } else {
4929 + errorCode = U_ILLEGAL_ARGUMENT_ERROR;
4930 + return OTHER;
4931 + }
4932 +}
4933 +
4934 +U_NAMESPACE_END
4935 +
4936 +#endif // !UCONFIG_NO_FORMATTING
4937 diff --git a/source/i18n/standardplural.h b/source/i18n/standardplural.h
4938 new file mode 100644
4939 index 0000000..8a8de21
4940 --- /dev/null
4941 +++ b/source/i18n/standardplural.h
4942 @@ -0,0 +1,130 @@
4943 +/*
4944 + ****************************************************************************** *
4945 + * Copyright (C) 2015, International Business Machines Corporation
4946 + * and others. All Rights Reserved.
4947 + ****************************************************************************** *
4948 + * standardplural.h
4949 + *
4950 + * created on: 2015dec14
4951 + * created by: Markus W. Scherer
4952 + */
4953 +
4954 +#ifndef __STANDARDPLURAL_H__
4955 +#define __STANDARDPLURAL_H__
4956 +
4957 +#include "unicode/utypes.h"
4958 +
4959 +#if !UCONFIG_NO_FORMATTING
4960 +
4961 +U_NAMESPACE_BEGIN
4962 +
4963 +class UnicodeString;
4964 +
4965 +/**
4966 + * Standard CLDR plural form/category constants.
4967 + * See http://www.unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Ru les
4968 + */
4969 +class U_I18N_API StandardPlural {
4970 +public:
4971 + enum Form {
4972 + ZERO,
4973 + ONE,
4974 + TWO,
4975 + FEW,
4976 + MANY,
4977 + OTHER,
4978 + COUNT
4979 + };
4980 +
4981 + /**
4982 + * @return the lowercase CLDR keyword string for the plural form
4983 + */
4984 + static const char *getKeyword(Form p);
4985 +
4986 + /**
4987 + * @param keyword for example "few" or "other"
4988 + * @return the plural form corresponding to the keyword, or OTHER
4989 + */
4990 + static Form orOtherFromString(const char *keyword) {
4991 + return static_cast<Form>(indexOrOtherIndexFromString(keyword));
4992 + }
4993 +
4994 + /**
4995 + * @param keyword for example "few" or "other"
4996 + * @return the plural form corresponding to the keyword, or OTHER
4997 + */
4998 + static Form orOtherFromString(const UnicodeString &keyword) {
4999 + return static_cast<Form>(indexOrOtherIndexFromString(keyword));
5000 + }
5001 +
5002 + /**
5003 + * Sets U_ILLEGAL_ARGUMENT_ERROR if the keyword is not a plural form.
5004 + *
5005 + * @param keyword for example "few" or "other"
5006 + * @return the plural form corresponding to the keyword
5007 + */
5008 + static Form fromString(const char *keyword, UErrorCode &errorCode) {
5009 + return static_cast<Form>(indexFromString(keyword, errorCode));
5010 + }
5011 +
5012 + /**
5013 + * Sets U_ILLEGAL_ARGUMENT_ERROR if the keyword is not a plural form.
5014 + *
5015 + * @param keyword for example "few" or "other"
5016 + * @return the plural form corresponding to the keyword
5017 + */
5018 + static Form fromString(const UnicodeString &keyword, UErrorCode &errorCode) {
5019 + return static_cast<Form>(indexFromString(keyword, errorCode));
5020 + }
5021 +
5022 + /**
5023 + * @param keyword for example "few" or "other"
5024 + * @return the index of the plural form corresponding to the keyword, or a negative value
5025 + */
5026 + static int32_t indexOrNegativeFromString(const char *keyword);
5027 +
5028 + /**
5029 + * @param keyword for example "few" or "other"
5030 + * @return the index of the plural form corresponding to the keyword, or a negative value
5031 + */
5032 + static int32_t indexOrNegativeFromString(const UnicodeString &keyword);
5033 +
5034 + /**
5035 + * @param keyword for example "few" or "other"
5036 + * @return the index of the plural form corresponding to the keyword, or OT HER
5037 + */
5038 + static int32_t indexOrOtherIndexFromString(const char *keyword) {
5039 + int32_t i = indexOrNegativeFromString(keyword);
5040 + return i >= 0 ? i : OTHER;
5041 + }
5042 +
5043 + /**
5044 + * @param keyword for example "few" or "other"
5045 + * @return the index of the plural form corresponding to the keyword, or OT HER
5046 + */
5047 + static int32_t indexOrOtherIndexFromString(const UnicodeString &keyword) {
5048 + int32_t i = indexOrNegativeFromString(keyword);
5049 + return i >= 0 ? i : OTHER;
5050 + }
5051 +
5052 + /**
5053 + * Sets U_ILLEGAL_ARGUMENT_ERROR if the keyword is not a plural form.
5054 + *
5055 + * @param keyword for example "few" or "other"
5056 + * @return the index of the plural form corresponding to the keyword
5057 + */
5058 + static int32_t indexFromString(const char *keyword, UErrorCode &errorCode);
5059 +
5060 + /**
5061 + * Sets U_ILLEGAL_ARGUMENT_ERROR if the keyword is not a plural form.
5062 + *
5063 + * @param keyword for example "few" or "other"
5064 + * @return the index of the plural form corresponding to the keyword
5065 + */
5066 + static int32_t indexFromString(const UnicodeString &keyword, UErrorCode &er rorCode);
5067 +};
5068 +
5069 +U_NAMESPACE_END
5070 +
5071 +#endif // !UCONFIG_NO_FORMATTING
5072 +#endif // __STANDARDPLURAL_H__
5073 diff --git a/source/i18n/unicode/measfmt.h b/source/i18n/unicode/measfmt.h
5074 index 61a1e86..ee05427 100644
5075 --- a/source/i18n/unicode/measfmt.h
5076 +++ b/source/i18n/unicode/measfmt.h
5077 @@ -327,17 +327,19 @@ class U_I18N_API MeasureFormat : public Format {
5078 // shared across instances.
5079 ListFormatter *listFormatter;
5080
5081 - const QuantityFormatter *getQuantityFormatter(
5082 - int32_t index,
5083 - int32_t widthIndex,
5084 - UErrorCode &status) const;
5085 + const SimplePatternFormatter *getFormatterOrNull(
5086 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const;
5087 +
5088 + const SimplePatternFormatter *getFormatter(
5089 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
5090 + UErrorCode &errorCode) const;
5091
5092 - const SimplePatternFormatter *getPerUnitFormatter(
5093 - int32_t index,
5094 - int32_t widthIndex) const;
5095 + const SimplePatternFormatter *getPluralFormatter(
5096 + const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
5097 + UErrorCode &errorCode) const;
5098
5099 const SimplePatternFormatter *getPerFormatter(
5100 - int32_t widthIndex,
5101 + UMeasureFormatWidth width,
5102 UErrorCode &status) const;
5103
5104 int32_t withPerUnitAndAppend(
5105 diff --git a/source/i18n/unicode/measunit.h b/source/i18n/unicode/measunit.h
5106 index 196832f..dc7c970 100644
5107 --- a/source/i18n/unicode/measunit.h
5108 +++ b/source/i18n/unicode/measunit.h
5109 @@ -185,6 +185,14 @@ class U_I18N_API MeasureUnit: public UObject {
5110
5111 /**
5112 * ICU use only.
5113 + * @return the unit.getIndex() of the unit which has this unit.getType() an d unit.getSubtype(),
5114 + * or a negative value if there is no such unit
5115 + * @internal
5116 + */
5117 + static int32_t internalGetIndexForTypeAndSubtype(const char *type, const ch ar *subtype);
5118 +
5119 + /**
5120 + * ICU use only.
5121 * @internal
5122 */
5123 static MeasureUnit *resolveUnitPerUnit(
5124 diff --git a/source/test/depstest/dependencies.txt b/source/test/depstest/depend encies.txt
5125 index d77c828..89ad39c 100644
5126 --- a/source/test/depstest/dependencies.txt
5127 +++ b/source/test/depstest/dependencies.txt
5128 @@ -559,7 +559,7 @@ group: ures_cnv # ures_openU, path is a Unicode string
5129 conversion resourcebundle
5130
5131 group: resourcebundle
5132 - resbund.o uresbund.o uresdata.o
5133 + resource.o resbund.o uresbund.o uresdata.o
5134 locavailable.o
5135 # uloc_tag.c and uloc_keytype.cpp convert between
5136 # old ICU/LDML/CLDR locale IDs and newer BCP 47 IDs.
5137 @@ -893,7 +893,7 @@ group: format
5138 resourcebundle parsepos uvector32
5139
5140 group: pluralrules
5141 - plurrule.o upluralrules.o
5142 + standardplural.o plurrule.o upluralrules.o
5143 deps
5144 digitlist # plurals depend on decimals
5145 patternprops resourcebundle uvector uvector32 unifiedcache
5146 diff --git a/source/test/intltest/itformat.cpp b/source/test/intltest/itformat.c pp
5147 index f9f815d..deae7ba 100644
5148 --- a/source/test/intltest/itformat.cpp
5149 +++ b/source/test/intltest/itformat.cpp
5150 @@ -53,7 +53,6 @@
5151 #include "plurfmts.h" // PluralFormatTest
5152 #include "selfmts.h" // PluralFormatTest
5153 #include "dtifmtts.h" // DateIntervalFormatTest
5154 -#include "tufmtts.h" // TimeUnitTest
5155 #include "locnmtst.h" // LocaleDisplayNamesTest
5156 #include "dcfmtest.h" // DecimalFormatTest
5157 #include "listformattertest.h" // ListFormatterTest
5158 @@ -64,6 +63,7 @@ extern IntlTest *createGenderInfoTest();
5159 #if !UCONFIG_NO_BREAK_ITERATION
5160 extern IntlTest *createRelativeDateTimeFormatterTest();
5161 #endif
5162 +extern IntlTest *createTimeUnitTest();
5163 extern IntlTest *createMeasureFormatTest();
5164 extern IntlTest *createNumberFormatSpecificationTest();
5165 extern IntlTest *createScientificNumberFormatterTest();
5166 @@ -139,7 +139,15 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool e xec, const char* &nam
5167 TESTCLASS(35,PluralRulesTest);
5168 TESTCLASS(36,PluralFormatTest);
5169 TESTCLASS(37,DateIntervalFormatTest);
5170 - TESTCLASS(38,TimeUnitTest);
5171 + case 38:
5172 + name = "TimeUnitTest";
5173 + if (exec) {
5174 + logln("TimeUnitTest test---");
5175 + logln((UnicodeString)"");
5176 + LocalPointer<IntlTest> test(createTimeUnitTest());
5177 + callTest(*test, par);
5178 + }
5179 + break;
5180 TESTCLASS(39,SelectFormatTest);
5181 TESTCLASS(40,LocaleDisplayNamesTest);
5182 #if !UCONFIG_NO_REGULAR_EXPRESSIONS
5183 diff --git a/source/test/intltest/itutil.cpp b/source/test/intltest/itutil.cpp
5184 index 49655fa..4c4f604 100644
5185 --- a/source/test/intltest/itutil.cpp
5186 +++ b/source/test/intltest/itutil.cpp
5187 @@ -124,6 +124,7 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
5188 LocalPointer<IntlTest> test(createQuantityFormatterTest());
5189 callTest(*test, par);
5190 }
5191 + break;
5192 case 23:
5193 name = "PluralMapTest";
5194 if (exec) {
5195 diff --git a/source/test/intltest/measfmttest.cpp b/source/test/intltest/measfmt test.cpp
5196 index f2f2d09..4732f61 100644
5197 --- a/source/test/intltest/measfmttest.cpp
5198 +++ b/source/test/intltest/measfmttest.cpp
5199 @@ -61,6 +61,7 @@ private:
5200 void TestGroupingSeparator();
5201 void TestDoubleZero();
5202 void TestUnitPerUnitResolution();
5203 + void TestIndividualPluralFallback();
5204 void verifyFormat(
5205 const char *description,
5206 const MeasureFormat &fmt,
5207 @@ -141,6 +142,7 @@ void MeasureFormatTest::runIndexedTest(
5208 TESTCASE_AUTO(TestGroupingSeparator);
5209 TESTCASE_AUTO(TestDoubleZero);
5210 TESTCASE_AUTO(TestUnitPerUnitResolution);
5211 + TESTCASE_AUTO(TestIndividualPluralFallback);
5212 TESTCASE_AUTO_END;
5213 }
5214
5215 @@ -1588,6 +1590,19 @@ void MeasureFormatTest::TestUnitPerUnitResolution() {
5216 assertEquals("", "50 psi", actual);
5217 }
5218
5219 +void MeasureFormatTest::TestIndividualPluralFallback() {
5220 + // See ticket #11986 "incomplete fallback in MeasureFormat".
5221 + // In CLDR 28, fr_CA temperature-generic/short has only the "one" form,
5222 + // and falls back to fr for the "other" form.
5223 + IcuTestErrorCode errorCode(*this, "TestIndividualPluralFallback");
5224 + MeasureFormat mf("fr_CA", UMEASFMT_WIDTH_SHORT, errorCode);
5225 + LocalPointer<Measure> twoDeg(
5226 + new Measure(2, MeasureUnit::createGenericTemperature(errorCode), errorC ode), errorCode);
5227 + UnicodeString expected = UNICODE_STRING_SIMPLE("2\\u00B0").unescape();
5228 + UnicodeString actual;
5229 + assertEquals("2 deg temp in fr_CA", expected, mf.format(twoDeg.orphan(), ac tual, errorCode));
5230 +}
5231 +
5232 void MeasureFormatTest::verifyFieldPosition(
5233 const char *description,
5234 const MeasureFormat &fmt,
5235 diff --git a/source/test/intltest/quantityformattertest.cpp b/source/test/intlte st/quantityformattertest.cpp
5236 index 0167075..c641de2 100644
5237 --- a/source/test/intltest/quantityformattertest.cpp
5238 +++ b/source/test/intltest/quantityformattertest.cpp
5239 @@ -1,7 +1,7 @@
5240 /*
5241 *******************************************************************************
5242 -* Copyright (C) 2014, International Business Machines Corporation and *
5243 -* others. All Rights Reserved. *
5244 +* Copyright (C) 2014-2015, International Business Machines Corporation and
5245 +* others. All Rights Reserved.
5246 *******************************************************************************
5247 *
5248 * File QUANTITYFORMATTERTEST.CPP
5249 @@ -37,25 +37,25 @@ void QuantityFormatterTest::TestBasic() {
5250 QuantityFormatter fmt;
5251 assertFalse(
5252 "adding bad variant",
5253 - fmt.add("a bad variant", "{0} pounds", status));
5254 + fmt.addIfAbsent("a bad variant", "{0} pounds", status));
5255 assertEquals("adding bad variant status", U_ILLEGAL_ARGUMENT_ERROR, status) ;
5256 status = U_ZERO_ERROR;
5257 assertFalse(
5258 "Adding bad pattern",
5259 - fmt.add("other", "{0} {1} too many placeholders", status));
5260 + fmt.addIfAbsent("other", "{0} {1} too many placeholders", status));
5261 assertEquals("adding bad pattern status", U_ILLEGAL_ARGUMENT_ERROR, status) ;
5262 status = U_ZERO_ERROR;
5263 assertFalse("isValid with no patterns", fmt.isValid());
5264 assertTrue(
5265 "Adding good pattern with no placeholders",
5266 - fmt.add("other", "no placeholder", status));
5267 + fmt.addIfAbsent("zero", "no placeholder", status));
5268 assertTrue(
5269 "Adding good pattern",
5270 - fmt.add("other", "{0} pounds", status));
5271 + fmt.addIfAbsent("other", "{0} pounds", status));
5272 assertTrue("isValid with other", fmt.isValid());
5273 assertTrue(
5274 "Adding good pattern",
5275 - fmt.add("one", "{0} pound", status));
5276 + fmt.addIfAbsent("one", "{0} pound", status));
5277
5278 assertEquals(
5279 "getByVariant",
5280 diff --git a/source/test/intltest/reldatefmttest.cpp b/source/test/intltest/reld atefmttest.cpp
5281 index 91f5483..f11adb6 100644
5282 --- a/source/test/intltest/reldatefmttest.cpp
5283 +++ b/source/test/intltest/reldatefmttest.cpp
5284 @@ -231,9 +231,9 @@ static WithQuantityExpected kSerbian[] = {
5285 };
5286
5287 static WithQuantityExpected kSerbianNarrow[] = {
5288 - {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 0 \\u0 43c\\u0435\\u0441."},
5289 - {1.2, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 1,2 \\ u043c\\u0435\\u0441."},
5290 - {21.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 21 \\ u043c\\u0435\\u0441."}
5291 + {0.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 0 \\u0 43c."},
5292 + {1.2, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 1,2 \\ u043c."},
5293 + {21.0, UDAT_DIRECTION_NEXT, UDAT_RELATIVE_MONTHS, "\\u0437\\u0430 21 \\ u043c."}
5294 };
5295
5296 static WithoutQuantityExpected kEnglishNoQuantity[] = {
5297 diff --git a/source/test/intltest/tufmtts.cpp b/source/test/intltest/tufmtts.cpp
5298 index 8ed9b9f..6b1ed5a 100644
5299 --- a/source/test/intltest/tufmtts.cpp
5300 +++ b/source/test/intltest/tufmtts.cpp
5301 @@ -1,5 +1,5 @@
5302 /********************************************************************
5303 - * Copyright (c) 2008-2014, International Business Machines Corporation and
5304 + * Copyright (c) 2008-2015, International Business Machines Corporation and
5305 * others. All Rights Reserved.
5306 ********************************************************************/
5307
5308 @@ -11,9 +11,9 @@
5309 #include "unicode/tmunit.h"
5310 #include "unicode/tmutamt.h"
5311 #include "unicode/tmutfmt.h"
5312 -#include "tufmtts.h"
5313 -#include "cmemory.h"
5314 #include "unicode/ustring.h"
5315 +#include "cmemory.h"
5316 +#include "intltest.h"
5317
5318 //TODO: put as compilation flag
5319 //#define TUFMTTS_DEBUG 1
5320 @@ -22,16 +22,60 @@
5321 #include <iostream>
5322 #endif
5323
5324 -void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name , char* /*par*/ ) {
5325 - if (exec) logln("TestSuite TimeUnitTest");
5326 - switch (index) {
5327 - TESTCASE(0, testBasic);
5328 - TESTCASE(1, testAPI);
5329 - TESTCASE(2, testGreekWithFallback);
5330 - TESTCASE(3, testGreekWithSanitization);
5331 - TESTCASE(4, test10219Plurals);
5332 - default: name = ""; break;
5333 +class TimeUnitTest : public IntlTest {
5334 + void runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*p ar*/ ) {
5335 + if (exec) logln("TestSuite TimeUnitTest");
5336 + TESTCASE_AUTO_BEGIN;
5337 + TESTCASE_AUTO(testBasic);
5338 + TESTCASE_AUTO(testAPI);
5339 + TESTCASE_AUTO(testGreekWithFallback);
5340 + TESTCASE_AUTO(testGreekWithSanitization);
5341 + TESTCASE_AUTO(test10219Plurals);
5342 + TESTCASE_AUTO(TestBritishShortHourFallback);
5343 + TESTCASE_AUTO_END;
5344 }
5345 +
5346 +public:
5347 + /**
5348 + * Performs basic tests
5349 + **/
5350 + void testBasic();
5351 +
5352 + /**
5353 + * Performs API tests
5354 + **/
5355 + void testAPI();
5356 +
5357 + /**
5358 + * Performs tests for Greek
5359 + * This tests that requests for short unit names correctly fall back
5360 + * to long unit names for a locale where the locale data does not
5361 + * provide short unit names. As of CLDR 1.9, Greek is one such language.
5362 + **/
5363 + void testGreekWithFallback();
5364 +
5365 + /**
5366 + * Performs tests for Greek
5367 + * This tests that if the plural count listed in time unit format does not
5368 + * match those in the plural rules for the locale, those plural count in
5369 + * time unit format will be ingored and subsequently, fall back will kick i n
5370 + * which is tested above.
5371 + * Without data sanitization, setNumberFormat() would crash.
5372 + * As of CLDR shiped in ICU4.8, Greek is one such language.
5373 + */
5374 + void testGreekWithSanitization();
5375 +
5376 + /**
5377 + * Performs unit test for ticket 10219 making sure that plurals work
5378 + * correctly with rounding.
5379 + */
5380 + void test10219Plurals();
5381 +
5382 + void TestBritishShortHourFallback();
5383 +};
5384 +
5385 +extern IntlTest *createTimeUnitTest() {
5386 + return new TimeUnitTest();
5387 }
5388
5389 // This function is more lenient than equals operator as it considers integer 3 hours and
5390 @@ -469,4 +513,16 @@ void TimeUnitTest::test10219Plurals() {
5391 }
5392 }
5393
5394 +void TimeUnitTest::TestBritishShortHourFallback() {
5395 + // See ticket #11986 "incomplete fallback in MeasureFormat".
5396 + UErrorCode status = U_ZERO_ERROR;
5397 + Formattable oneHour(new TimeUnitAmount(1, TimeUnit::UTIMEUNIT_HOUR, status) );
5398 + Locale en_GB("en_GB");
5399 + TimeUnitFormat formatter(en_GB, UTMUTFMT_ABBREVIATED_STYLE, status);
5400 + UnicodeString result;
5401 + formatter.format(oneHour, result, status);
5402 + assertSuccess("TestBritishShortHourFallback()", status);
5403 + assertEquals("TestBritishShortHourFallback()", UNICODE_STRING_SIMPLE("1 hr" ), result);
5404 +}
5405 +
5406 #endif
5407 diff --git a/source/test/intltest/tufmtts.h b/source/test/intltest/tufmtts.h
5408 index cd4a485..e69de29 100644
5409 --- a/source/test/intltest/tufmtts.h
5410 +++ b/source/test/intltest/tufmtts.h
5411 @@ -1,63 +0,0 @@
5412 -/********************************************************************
5413 - * COPYRIGHT:
5414 - * Copyright (c) 2008-2013, International Business Machines Corporation
5415 - * and others. All Rights Reserved.
5416 - ********************************************************************/
5417 -
5418 -#ifndef __INTLTESTTIMEUNITTEST__
5419 -#define __INTLTESTTIMEUNITTEST__
5420 -
5421 -
5422 -#if !UCONFIG_NO_FORMATTING
5423 -
5424 -#include "unicode/utypes.h"
5425 -#include "unicode/locid.h"
5426 -#include "intltest.h"
5427 -
5428 -/**
5429 - * Test basic functionality of various API functions
5430 - **/
5431 -class TimeUnitTest: public IntlTest {
5432 - void runIndexedTest( int32_t index, UBool exec, const char* &name, char* pa r = NULL );
5433 -
5434 -public:
5435 - /**
5436 - * Performs basic tests
5437 - **/
5438 - void testBasic();
5439 -
5440 - /**
5441 - * Performs API tests
5442 - **/
5443 - void testAPI();
5444 -
5445 - /**
5446 - * Performs tests for Greek
5447 - * This tests that requests for short unit names correctly fall back
5448 - * to long unit names for a locale where the locale data does not
5449 - * provide short unit names. As of CLDR 1.9, Greek is one such language.
5450 - **/
5451 - void testGreekWithFallback();
5452 -
5453 - /**
5454 - * Performs tests for Greek
5455 - * This tests that if the plural count listed in time unit format does not
5456 - * match those in the plural rules for the locale, those plural count in
5457 - * time unit format will be ingored and subsequently, fall back will kick i n
5458 - * which is tested above.
5459 - * Without data sanitization, setNumberFormat() would crash.
5460 - * As of CLDR shiped in ICU4.8, Greek is one such language.
5461 - */
5462 - void testGreekWithSanitization();
5463 -
5464 - /**
5465 - * Performs unit test for ticket 10219 making sure that plurals work
5466 - * correctly with rounding.
5467 - */
5468 - void test10219Plurals();
5469 -
5470 -};
5471 -
5472 -#endif /* #if !UCONFIG_NO_FORMATTING */
5473 -
5474 -#endif
OLDNEW
« no previous file with comments | « patches/locid.patch ('k') | patches/mutex.patch » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698