/src/icu/source/common/resbund.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  | **********************************************************************  | 
5  |  | *   Copyright (C) 1997-2013, International Business Machines  | 
6  |  | *   Corporation and others.  All Rights Reserved.  | 
7  |  | **********************************************************************  | 
8  |  | *  | 
9  |  | * File resbund.cpp  | 
10  |  | *  | 
11  |  | * Modification History:  | 
12  |  | *  | 
13  |  | *   Date        Name        Description  | 
14  |  | *   02/05/97    aliu        Fixed bug in chopLocale.  Added scanForLocaleInFile  | 
15  |  | *                           based on code taken from scanForLocale.  Added  | 
16  |  | *                           constructor which attempts to read resource bundle  | 
17  |  | *                           from a specific file, without searching other files.  | 
18  |  | *   02/11/97    aliu        Added UErrorCode return values to constructors. Fixed  | 
19  |  | *                           infinite loops in scanForFile and scanForLocale.  | 
20  |  | *                           Modified getRawResourceData to not delete storage in  | 
21  |  | *                           localeData and resourceData which it doesn't own.  | 
22  |  | *                           Added Mac compatibility #ifdefs for tellp() and  | 
23  |  | *                           ios::nocreate.  | 
24  |  | *   03/04/97    aliu        Modified to use ExpandingDataSink objects instead of  | 
25  |  | *                           the highly inefficient ostrstream objects.  | 
26  |  | *   03/13/97    aliu        Rewrote to load in entire resource bundle and store  | 
27  |  | *                           it as a Hashtable of ResourceBundleData objects.  | 
28  |  | *                           Added state table to govern parsing of files.  | 
29  |  | *                           Modified to load locale index out of new file distinct  | 
30  |  | *                           from default.txt.  | 
31  |  | *   03/25/97    aliu        Modified to support 2-d arrays, needed for timezone data.  | 
32  |  | *                           Added support for custom file suffixes.  Again, needed  | 
33  |  | *                           to support timezone data.  Improved error handling to  | 
34  |  | *                           detect duplicate tags and subtags.  | 
35  |  | *   04/07/97    aliu        Fixed bug in getHashtableForLocale().  Fixed handling  | 
36  |  | *                           of failing UErrorCode values on entry to API methods.  | 
37  |  | *                           Fixed bugs in getArrayItem() for negative indices.  | 
38  |  | *   04/29/97    aliu        Update to use new Hashtable deletion protocol.  | 
39  |  | *   05/06/97    aliu        Flattened kTransitionTable for HP compiler.  | 
40  |  | *                           Fixed usage of CharString.  | 
41  |  | * 06/11/99      stephen     Removed parsing of .txt files.  | 
42  |  | *                           Reworked to use new binary format.  | 
43  |  | *                           Cleaned up.  | 
44  |  | * 06/14/99      stephen     Removed methods taking a filename suffix.  | 
45  |  | * 06/22/99      stephen     Added missing T_FileStream_close in parse()  | 
46  |  | * 11/09/99      weiv        Added getLocale(), rewritten constructForLocale()  | 
47  |  | * March 2000    weiv        complete overhaul.  | 
48  |  | ******************************************************************************  | 
49  |  | */  | 
50  |  |  | 
51  |  | #include "unicode/utypes.h"  | 
52  |  | #include "unicode/resbund.h"  | 
53  |  |  | 
54  |  | #include "cmemory.h"  | 
55  |  | #include "mutex.h"  | 
56  |  | #include "uassert.h"  | 
57  |  | #include "umutex.h"  | 
58  |  |  | 
59  |  | #include "uresimp.h"  | 
60  |  |  | 
61  |  | U_NAMESPACE_BEGIN  | 
62  |  |  | 
63  |  | /*-----------------------------------------------------------------------------  | 
64  |  |  * Implementation Notes  | 
65  |  |  *  | 
66  |  |  * Resource bundles are read in once, and thereafter cached.  | 
67  |  |  * ResourceBundle statically keeps track of which files have been  | 
68  |  |  * read, so we are guaranteed that each file is read at most once.  | 
69  |  |  * Resource bundles can be loaded from different data directories and  | 
70  |  |  * will be treated as distinct, even if they are for the same locale.  | 
71  |  |  *  | 
72  |  |  * Resource bundles are lightweight objects, which have pointers to  | 
73  |  |  * one or more shared Hashtable objects containing all the data.  | 
74  |  |  * Copying would be cheap, but there is no copy constructor, since  | 
75  |  |  * there wasn't one in the original API.  | 
76  |  |  *  | 
77  |  |  * The ResourceBundle parsing mechanism is implemented as a transition  | 
78  |  |  * network, for easy maintenance and modification.  The network is  | 
79  |  |  * implemented as a matrix (instead of in code) to make this even  | 
80  |  |  * easier.  The matrix contains Transition objects.  Each Transition  | 
81  |  |  * object describes a destination node and an action to take before  | 
82  |  |  * moving to the destination node.  The source node is encoded by the  | 
83  |  |  * index of the object in the array that contains it.  The pieces  | 
84  |  |  * needed to understand the transition network are the enums for node  | 
85  |  |  * IDs and actions, the parse() method, which walks through the  | 
86  |  |  * network and implements the actions, and the network itself.  The  | 
87  |  |  * network guarantees certain conditions, for example, that a new  | 
88  |  |  * resource will not be closed until one has been opened first; or  | 
89  |  |  * that data will not be stored into a TaggedList until a TaggedList  | 
90  |  |  * has been created.  Nonetheless, the code in parse() does some  | 
91  |  |  * consistency checks as it runs the network, and fails with an  | 
92  |  |  * U_INTERNAL_PROGRAM_ERROR if one of these checks fails.  If the input  | 
93  |  |  * data has a bad format, an U_INVALID_FORMAT_ERROR is returned.  If you  | 
94  |  |  * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in  | 
95  |  |  * it.  | 
96  |  |  *  | 
97  |  |  * Old functionality of multiple locales in a single file is still  | 
98  |  |  * supported.  For this reason, LOCALE names override FILE names.  If  | 
99  |  |  * data for en_US is located in the en.txt file, once it is loaded,  | 
100  |  |  * the code will not care where it came from (other than remembering  | 
101  |  |  * which directory it came from).  However, if there is an en_US  | 
102  |  |  * resource in en_US.txt, that will take precedence.  There is no  | 
103  |  |  * limit to the number or type of resources that can be stored in a  | 
104  |  |  * file, however, files are only searched in a specific way.  If  | 
105  |  |  * en_US_CA is requested, then first en_US_CA.txt is searched, then  | 
106  |  |  * en_US.txt, then en.txt, then default.txt.  So it only makes sense  | 
107  |  |  * to put certain locales in certain files.  In this example, it would  | 
108  |  |  * be logical to put en_US_CA, en_US, and en into the en.txt file,  | 
109  |  |  * since they would be found there if asked for.  The extreme example  | 
110  |  |  * is to place all locale resources into default.txt, which should  | 
111  |  |  * also work.  | 
112  |  |  *  | 
113  |  |  * Inheritance is implemented.  For example, xx_YY_zz inherits as  | 
114  |  |  * follows: xx_YY_zz, xx_YY, xx, default.  Inheritance is implemented  | 
115  |  |  * as an array of hashtables.  There will be from 1 to 4 hashtables in  | 
116  |  |  * the array.  | 
117  |  |  *  | 
118  |  |  * Fallback files are implemented.  The fallback pattern is Language  | 
119  |  |  * Country Variant (LCV) -> LC -> L.  Fallback is first done for the  | 
120  |  |  * requested locale.  Then it is done for the default locale, as  | 
121  |  |  * returned by Locale::getDefault().  Then the special file  | 
122  |  |  * default.txt is searched for the default locale.  The overall FILE  | 
123  |  |  * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default.  | 
124  |  |  *  | 
125  |  |  * Note that although file name searching includes the default locale,  | 
126  |  |  * once a ResourceBundle object is constructed, the inheritance path  | 
127  |  |  * no longer includes the default locale.  The path is LCV -> LC -> L  | 
128  |  |  * -> default.  | 
129  |  |  *  | 
130  |  |  * File parsing is lazy.  Nothing is parsed unless it is called for by  | 
131  |  |  * someone.  So when a ResourceBundle for xx_YY_zz is constructed,  | 
132  |  |  * only that locale is parsed (along with anything else in the same  | 
133  |  |  * file).  Later, if the FooBar tag is asked for, and if it isn't  | 
134  |  |  * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and  | 
135  |  |  * so forth, until the chain is exhausted or the tag is found.  | 
136  |  |  *  | 
137  |  |  * Thread-safety is implemented around caches, both the cache that  | 
138  |  |  * stores all the resource data, and the cache that stores flags  | 
139  |  |  * indicating whether or not a file has been visited.  These caches  | 
140  |  |  * delete their storage at static cleanup time, when the process  | 
141  |  |  * quits.  | 
142  |  |  *  | 
143  |  |  * ResourceBundle supports TableCollation as a special case.  This  | 
144  |  |  * involves having special ResourceBundle objects which DO own their  | 
145  |  |  * data, since we don't want large collation rule strings in the  | 
146  |  |  * ResourceBundle cache (these are already cached in the  | 
147  |  |  * TableCollation cache).  TableCollation files (.ctx files) have the  | 
148  |  |  * same format as normal resource data files, with a different  | 
149  |  |  * interpretation, from the standpoint of ResourceBundle.  .ctx files  | 
150  |  |  * are loaded into otherwise ordinary ResourceBundle objects.  They  | 
151  |  |  * don't inherit (that's implemented by TableCollation) and they own  | 
152  |  |  * their data (as mentioned above).  However, they still support  | 
153  |  |  * possible multiple locales in a single .ctx file.  (This is in  | 
154  |  |  * practice a bad idea, since you only want the one locale you're  | 
155  |  |  * looking for, and only one tag will be present  | 
156  |  |  * ("CollationElements"), so you don't need an inheritance chain of | 
157  |  |  * multiple locales.)  Up to 4 locale resources will be loaded from a  | 
158  |  |  * .ctx file; everything after the first 4 is ignored (parsed and  | 
159  |  |  * deleted).  (Normal .txt files have no limit.)  Instead of being  | 
160  |  |  * loaded into the cache, and then looked up as needed, the locale  | 
161  |  |  * resources are read straight into the ResourceBundle object.  | 
162  |  |  *  | 
163  |  |  * The Index, which used to reside in default.txt, has been moved to a  | 
164  |  |  * new file, index.txt.  This file contains a slightly modified format  | 
165  |  |  * with the addition of the "InstalledLocales" tag; it looks like:  | 
166  |  |  *  | 
167  |  |  * Index { | 
168  |  |  *   InstalledLocales { | 
169  |  |  *     ar  | 
170  |  |  *     ..  | 
171  |  |  *     zh_TW  | 
172  |  |  *   }  | 
173  |  |  * }  | 
174  |  |  */  | 
175  |  | //-----------------------------------------------------------------------------  | 
176  |  |  | 
177  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)  | 
178  |  |  | 
179  |  | ResourceBundle::ResourceBundle(UErrorCode &err)  | 
180  | 0  |                                 :UObject(), fLocale(NULL)  | 
181  | 0  | { | 
182  | 0  |     fResource = ures_open(0, Locale::getDefault().getName(), &err);  | 
183  | 0  | }  | 
184  |  |  | 
185  |  | ResourceBundle::ResourceBundle(const ResourceBundle &other)  | 
186  | 0  |                               :UObject(other), fLocale(NULL)  | 
187  | 0  | { | 
188  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
189  |  | 
  | 
190  | 0  |     if (other.fResource) { | 
191  | 0  |         fResource = ures_copyResb(0, other.fResource, &status);  | 
192  | 0  |     } else { | 
193  |  |         /* Copying a bad resource bundle */  | 
194  | 0  |         fResource = NULL;  | 
195  | 0  |     }  | 
196  | 0  | }  | 
197  |  |  | 
198  |  | ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err)  | 
199  | 0  |                                :UObject(), fLocale(NULL)  | 
200  | 0  | { | 
201  | 0  |     if (res) { | 
202  | 0  |         fResource = ures_copyResb(0, res, &err);  | 
203  | 0  |     } else { | 
204  |  |         /* Copying a bad resource bundle */  | 
205  | 0  |         fResource = NULL;  | 
206  | 0  |     }  | 
207  | 0  | }  | 
208  |  |  | 
209  |  | ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err)   | 
210  | 0  |                                :UObject(), fLocale(NULL)  | 
211  | 0  | { | 
212  | 0  |     fResource = ures_open(path, locale.getName(), &err);  | 
213  | 0  | }  | 
214  |  |  | 
215  |  |  | 
216  |  | ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other)  | 
217  | 0  | { | 
218  | 0  |     if(this == &other) { | 
219  | 0  |         return *this;  | 
220  | 0  |     }  | 
221  | 0  |     if(fResource != 0) { | 
222  | 0  |         ures_close(fResource);  | 
223  | 0  |         fResource = NULL;  | 
224  | 0  |     }  | 
225  | 0  |     if (fLocale != NULL) { | 
226  | 0  |         delete fLocale;  | 
227  | 0  |         fLocale = NULL;  | 
228  | 0  |     }  | 
229  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
230  | 0  |     if (other.fResource) { | 
231  | 0  |         fResource = ures_copyResb(0, other.fResource, &status);  | 
232  | 0  |     } else { | 
233  |  |         /* Copying a bad resource bundle */  | 
234  | 0  |         fResource = NULL;  | 
235  | 0  |     }  | 
236  | 0  |     return *this;  | 
237  | 0  | }  | 
238  |  |  | 
239  |  | ResourceBundle::~ResourceBundle()  | 
240  | 0  | { | 
241  | 0  |     if(fResource != 0) { | 
242  | 0  |         ures_close(fResource);  | 
243  | 0  |     }  | 
244  | 0  |     if(fLocale != NULL) { | 
245  | 0  |       delete(fLocale);  | 
246  | 0  |     }  | 
247  | 0  | }  | 
248  |  |  | 
249  |  | ResourceBundle *  | 
250  | 0  | ResourceBundle::clone() const { | 
251  | 0  |     return new ResourceBundle(*this);  | 
252  | 0  | }  | 
253  |  |  | 
254  | 0  | UnicodeString ResourceBundle::getString(UErrorCode& status) const { | 
255  | 0  |     int32_t len = 0;  | 
256  | 0  |     const UChar *r = ures_getString(fResource, &len, &status);  | 
257  | 0  |     return UnicodeString(TRUE, r, len);  | 
258  | 0  | }  | 
259  |  |  | 
260  | 0  | const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const { | 
261  | 0  |     return ures_getBinary(fResource, &len, &status);  | 
262  | 0  | }  | 
263  |  |  | 
264  | 0  | const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const { | 
265  | 0  |     return ures_getIntVector(fResource, &len, &status);  | 
266  | 0  | }  | 
267  |  |  | 
268  | 0  | uint32_t ResourceBundle::getUInt(UErrorCode& status) const { | 
269  | 0  |     return ures_getUInt(fResource, &status);  | 
270  | 0  | }  | 
271  |  |  | 
272  | 0  | int32_t ResourceBundle::getInt(UErrorCode& status) const { | 
273  | 0  |     return ures_getInt(fResource, &status);  | 
274  | 0  | }  | 
275  |  |  | 
276  | 0  | const char *ResourceBundle::getName(void) const { | 
277  | 0  |     return ures_getName(fResource);  | 
278  | 0  | }  | 
279  |  |  | 
280  | 0  | const char *ResourceBundle::getKey(void) const { | 
281  | 0  |     return ures_getKey(fResource);  | 
282  | 0  | }  | 
283  |  |  | 
284  | 0  | UResType ResourceBundle::getType(void) const { | 
285  | 0  |     return ures_getType(fResource);  | 
286  | 0  | }  | 
287  |  |  | 
288  | 0  | int32_t ResourceBundle::getSize(void) const { | 
289  | 0  |     return ures_getSize(fResource);  | 
290  | 0  | }  | 
291  |  |  | 
292  | 0  | UBool ResourceBundle::hasNext(void) const { | 
293  | 0  |     return ures_hasNext(fResource);  | 
294  | 0  | }  | 
295  |  |  | 
296  | 0  | void ResourceBundle::resetIterator(void) { | 
297  | 0  |     ures_resetIterator(fResource);  | 
298  | 0  | }  | 
299  |  |  | 
300  | 0  | ResourceBundle ResourceBundle::getNext(UErrorCode& status) { | 
301  | 0  |     UResourceBundle r;  | 
302  |  | 
  | 
303  | 0  |     ures_initStackObject(&r);  | 
304  | 0  |     ures_getNextResource(fResource, &r, &status);  | 
305  | 0  |     ResourceBundle res(&r, status);  | 
306  | 0  |     if (U_SUCCESS(status)) { | 
307  | 0  |         ures_close(&r);  | 
308  | 0  |     }  | 
309  | 0  |     return res;  | 
310  | 0  | }  | 
311  |  |  | 
312  | 0  | UnicodeString ResourceBundle::getNextString(UErrorCode& status) { | 
313  | 0  |     int32_t len = 0;  | 
314  | 0  |     const UChar* r = ures_getNextString(fResource, &len, 0, &status);  | 
315  | 0  |     return UnicodeString(TRUE, r, len);  | 
316  | 0  | }  | 
317  |  |  | 
318  | 0  | UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) { | 
319  | 0  |     int32_t len = 0;  | 
320  | 0  |     const UChar* r = ures_getNextString(fResource, &len, key, &status);  | 
321  | 0  |     return UnicodeString(TRUE, r, len);  | 
322  | 0  | }  | 
323  |  |  | 
324  | 0  | ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const { | 
325  | 0  |     UResourceBundle r;  | 
326  |  | 
  | 
327  | 0  |     ures_initStackObject(&r);  | 
328  | 0  |     ures_getByIndex(fResource, indexR, &r, &status);  | 
329  | 0  |     ResourceBundle res(&r, status);  | 
330  | 0  |     if (U_SUCCESS(status)) { | 
331  | 0  |         ures_close(&r);  | 
332  | 0  |     }  | 
333  | 0  |     return res;  | 
334  | 0  | }  | 
335  |  |  | 
336  | 0  | UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const { | 
337  | 0  |     int32_t len = 0;  | 
338  | 0  |     const UChar* r = ures_getStringByIndex(fResource, indexS, &len, &status);  | 
339  | 0  |     return UnicodeString(TRUE, r, len);  | 
340  | 0  | }  | 
341  |  |  | 
342  | 0  | ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const { | 
343  | 0  |     UResourceBundle r;  | 
344  |  | 
  | 
345  | 0  |     ures_initStackObject(&r);  | 
346  | 0  |     ures_getByKey(fResource, key, &r, &status);  | 
347  | 0  |     ResourceBundle res(&r, status);  | 
348  | 0  |     if (U_SUCCESS(status)) { | 
349  | 0  |         ures_close(&r);  | 
350  | 0  |     }  | 
351  | 0  |     return res;  | 
352  | 0  | }  | 
353  |  |  | 
354  | 0  | ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){ | 
355  | 0  |     UResourceBundle r;  | 
356  | 0  |     ures_initStackObject(&r);  | 
357  | 0  |     ures_getByKeyWithFallback(fResource, key, &r, &status);  | 
358  | 0  |     ResourceBundle res(&r, status);  | 
359  | 0  |     if(U_SUCCESS(status)){ | 
360  | 0  |         ures_close(&r);  | 
361  | 0  |     }  | 
362  | 0  |     return res;  | 
363  | 0  | }  | 
364  | 0  | UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const { | 
365  | 0  |     int32_t len = 0;  | 
366  | 0  |     const UChar* r = ures_getStringByKey(fResource, key, &len, &status);  | 
367  | 0  |     return UnicodeString(TRUE, r, len);  | 
368  | 0  | }  | 
369  |  |  | 
370  |  | const char*  | 
371  |  | ResourceBundle::getVersionNumber()  const  | 
372  | 0  | { | 
373  | 0  |     return ures_getVersionNumberInternal(fResource);  | 
374  | 0  | }  | 
375  |  |  | 
376  | 0  | void ResourceBundle::getVersion(UVersionInfo versionInfo) const { | 
377  | 0  |     ures_getVersion(fResource, versionInfo);  | 
378  | 0  | }  | 
379  |  |  | 
380  | 0  | const Locale &ResourceBundle::getLocale(void) const { | 
381  | 0  |     static UMutex gLocaleLock;  | 
382  | 0  |     Mutex lock(&gLocaleLock);  | 
383  | 0  |     if (fLocale != NULL) { | 
384  | 0  |         return *fLocale;  | 
385  | 0  |     }  | 
386  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
387  | 0  |     const char *localeName = ures_getLocaleInternal(fResource, &status);  | 
388  | 0  |     ResourceBundle *ncThis = const_cast<ResourceBundle *>(this);  | 
389  | 0  |     ncThis->fLocale = new Locale(localeName);  | 
390  | 0  |     return ncThis->fLocale != NULL ? *ncThis->fLocale : Locale::getDefault();  | 
391  | 0  | }  | 
392  |  |  | 
393  |  | const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const  | 
394  | 0  | { | 
395  | 0  |   return ures_getLocaleByType(fResource, type, &status);  | 
396  | 0  | }  | 
397  |  |  | 
398  |  | U_NAMESPACE_END  | 
399  |  | //eof  |