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 resouce 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 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)177 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)
178 
179 ResourceBundle::ResourceBundle(UErrorCode &err)
180                                 :UObject(), fLocale(NULL)
181 {
182     fResource = ures_open(0, Locale::getDefault().getName(), &err);
183 }
184 
ResourceBundle(const ResourceBundle & other)185 ResourceBundle::ResourceBundle(const ResourceBundle &other)
186                               :UObject(other), fLocale(NULL)
187 {
188     UErrorCode status = U_ZERO_ERROR;
189 
190     if (other.fResource) {
191         fResource = ures_copyResb(0, other.fResource, &status);
192     } else {
193         /* Copying a bad resource bundle */
194         fResource = NULL;
195     }
196 }
197 
ResourceBundle(UResourceBundle * res,UErrorCode & err)198 ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err)
199                                :UObject(), fLocale(NULL)
200 {
201     if (res) {
202         fResource = ures_copyResb(0, res, &err);
203     } else {
204         /* Copying a bad resource bundle */
205         fResource = NULL;
206     }
207 }
208 
ResourceBundle(const char * path,const Locale & locale,UErrorCode & err)209 ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err)
210                                :UObject(), fLocale(NULL)
211 {
212     fResource = ures_open(path, locale.getName(), &err);
213 }
214 
215 
operator =(const ResourceBundle & other)216 ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other)
217 {
218     if(this == &other) {
219         return *this;
220     }
221     if(fResource != 0) {
222         ures_close(fResource);
223         fResource = NULL;
224     }
225     if (fLocale != NULL) {
226         delete fLocale;
227         fLocale = NULL;
228     }
229     UErrorCode status = U_ZERO_ERROR;
230     if (other.fResource) {
231         fResource = ures_copyResb(0, other.fResource, &status);
232     } else {
233         /* Copying a bad resource bundle */
234         fResource = NULL;
235     }
236     return *this;
237 }
238 
~ResourceBundle()239 ResourceBundle::~ResourceBundle()
240 {
241     if(fResource != 0) {
242         ures_close(fResource);
243     }
244     if(fLocale != NULL) {
245       delete(fLocale);
246     }
247 }
248 
249 ResourceBundle *
clone() const250 ResourceBundle::clone() const {
251     return new ResourceBundle(*this);
252 }
253 
getString(UErrorCode & status) const254 UnicodeString ResourceBundle::getString(UErrorCode& status) const {
255     int32_t len = 0;
256     const UChar *r = ures_getString(fResource, &len, &status);
257     return UnicodeString(TRUE, r, len);
258 }
259 
getBinary(int32_t & len,UErrorCode & status) const260 const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const {
261     return ures_getBinary(fResource, &len, &status);
262 }
263 
getIntVector(int32_t & len,UErrorCode & status) const264 const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const {
265     return ures_getIntVector(fResource, &len, &status);
266 }
267 
getUInt(UErrorCode & status) const268 uint32_t ResourceBundle::getUInt(UErrorCode& status) const {
269     return ures_getUInt(fResource, &status);
270 }
271 
getInt(UErrorCode & status) const272 int32_t ResourceBundle::getInt(UErrorCode& status) const {
273     return ures_getInt(fResource, &status);
274 }
275 
getName(void) const276 const char *ResourceBundle::getName(void) const {
277     return ures_getName(fResource);
278 }
279 
getKey(void) const280 const char *ResourceBundle::getKey(void) const {
281     return ures_getKey(fResource);
282 }
283 
getType(void) const284 UResType ResourceBundle::getType(void) const {
285     return ures_getType(fResource);
286 }
287 
getSize(void) const288 int32_t ResourceBundle::getSize(void) const {
289     return ures_getSize(fResource);
290 }
291 
hasNext(void) const292 UBool ResourceBundle::hasNext(void) const {
293     return ures_hasNext(fResource);
294 }
295 
resetIterator(void)296 void ResourceBundle::resetIterator(void) {
297     ures_resetIterator(fResource);
298 }
299 
getNext(UErrorCode & status)300 ResourceBundle ResourceBundle::getNext(UErrorCode& status) {
301     UResourceBundle r;
302 
303     ures_initStackObject(&r);
304     ures_getNextResource(fResource, &r, &status);
305     ResourceBundle res(&r, status);
306     if (U_SUCCESS(status)) {
307         ures_close(&r);
308     }
309     return res;
310 }
311 
getNextString(UErrorCode & status)312 UnicodeString ResourceBundle::getNextString(UErrorCode& status) {
313     int32_t len = 0;
314     const UChar* r = ures_getNextString(fResource, &len, 0, &status);
315     return UnicodeString(TRUE, r, len);
316 }
317 
getNextString(const char ** key,UErrorCode & status)318 UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) {
319     int32_t len = 0;
320     const UChar* r = ures_getNextString(fResource, &len, key, &status);
321     return UnicodeString(TRUE, r, len);
322 }
323 
get(int32_t indexR,UErrorCode & status) const324 ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const {
325     UResourceBundle r;
326 
327     ures_initStackObject(&r);
328     ures_getByIndex(fResource, indexR, &r, &status);
329     ResourceBundle res(&r, status);
330     if (U_SUCCESS(status)) {
331         ures_close(&r);
332     }
333     return res;
334 }
335 
getStringEx(int32_t indexS,UErrorCode & status) const336 UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const {
337     int32_t len = 0;
338     const UChar* r = ures_getStringByIndex(fResource, indexS, &len, &status);
339     return UnicodeString(TRUE, r, len);
340 }
341 
get(const char * key,UErrorCode & status) const342 ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const {
343     UResourceBundle r;
344 
345     ures_initStackObject(&r);
346     ures_getByKey(fResource, key, &r, &status);
347     ResourceBundle res(&r, status);
348     if (U_SUCCESS(status)) {
349         ures_close(&r);
350     }
351     return res;
352 }
353 
getWithFallback(const char * key,UErrorCode & status)354 ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){
355     UResourceBundle r;
356     ures_initStackObject(&r);
357     ures_getByKeyWithFallback(fResource, key, &r, &status);
358     ResourceBundle res(&r, status);
359     if(U_SUCCESS(status)){
360         ures_close(&r);
361     }
362     return res;
363 }
getStringEx(const char * key,UErrorCode & status) const364 UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const {
365     int32_t len = 0;
366     const UChar* r = ures_getStringByKey(fResource, key, &len, &status);
367     return UnicodeString(TRUE, r, len);
368 }
369 
370 const char*
getVersionNumber() const371 ResourceBundle::getVersionNumber()  const
372 {
373     return ures_getVersionNumberInternal(fResource);
374 }
375 
getVersion(UVersionInfo versionInfo) const376 void ResourceBundle::getVersion(UVersionInfo versionInfo) const {
377     ures_getVersion(fResource, versionInfo);
378 }
379 
getLocale(void) const380 const Locale &ResourceBundle::getLocale(void) const {
381     static UMutex gLocaleLock;
382     Mutex lock(&gLocaleLock);
383     if (fLocale != NULL) {
384         return *fLocale;
385     }
386     UErrorCode status = U_ZERO_ERROR;
387     const char *localeName = ures_getLocaleInternal(fResource, &status);
388     ResourceBundle *ncThis = const_cast<ResourceBundle *>(this);
389     ncThis->fLocale = new Locale(localeName);
390     return ncThis->fLocale != NULL ? *ncThis->fLocale : Locale::getDefault();
391 }
392 
getLocale(ULocDataLocaleType type,UErrorCode & status) const393 const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const
394 {
395   return ures_getLocaleByType(fResource, type, &status);
396 }
397 
398 U_NAMESPACE_END
399 //eof
400