OLD | NEW |
| (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 & resources</Filter> | |
43 </ClCompile> | |
44 - <ClCompile Include="uresdata.c"> | |
45 + <ClCompile Include="uresdata.cpp"> | |
46 + <Filter>locales & resources</Filter> | |
47 + </ClCompile> | |
48 + <ClCompile Include="resource.cpp"> | |
49 <Filter>locales & resources</Filter> | |
50 </ClCompile> | |
51 <ClCompile Include="caniter.cpp"> | |
52 @@ -769,6 +772,9 @@ | |
53 <ClInclude Include="uresdata.h"> | |
54 <Filter>locales & resources</Filter> | |
55 </ClInclude> | |
56 + <ClInclude Include="resource.h"> | |
57 + <Filter>locales & resources</Filter> | |
58 + </ClInclude> | |
59 <ClInclude Include="uresimp.h"> | |
60 <Filter>locales & 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(¶m, 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 | |
OLD | NEW |