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