• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)177 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)
178 
179 ResourceBundle::ResourceBundle(UErrorCode &err)
180                                 :UObject(), fLocale(nullptr)
181 {
182     fResource = ures_open(nullptr, Locale::getDefault().getName(), &err);
183 }
184 
ResourceBundle(const ResourceBundle & other)185 ResourceBundle::ResourceBundle(const ResourceBundle &other)
186                               :UObject(other), fLocale(nullptr)
187 {
188     UErrorCode status = U_ZERO_ERROR;
189 
190     if (other.fResource) {
191         fResource = ures_copyResb(nullptr, other.fResource, &status);
192     } else {
193         /* Copying a bad resource bundle */
194         fResource = nullptr;
195     }
196 }
197 
ResourceBundle(UResourceBundle * res,UErrorCode & err)198 ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err)
199                                :UObject(), fLocale(nullptr)
200 {
201     if (res) {
202         fResource = ures_copyResb(nullptr, res, &err);
203     } else {
204         /* Copying a bad resource bundle */
205         fResource = nullptr;
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(nullptr)
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 != nullptr) {
222         ures_close(fResource);
223         fResource = nullptr;
224     }
225     if (fLocale != nullptr) {
226         delete fLocale;
227         fLocale = nullptr;
228     }
229     UErrorCode status = U_ZERO_ERROR;
230     if (other.fResource) {
231         fResource = ures_copyResb(nullptr, other.fResource, &status);
232     } else {
233         /* Copying a bad resource bundle */
234         fResource = nullptr;
235     }
236     return *this;
237 }
238 
~ResourceBundle()239 ResourceBundle::~ResourceBundle()
240 {
241     if (fResource != nullptr) {
242         ures_close(fResource);
243     }
244     delete fLocale;
245 }
246 
247 ResourceBundle *
clone() const248 ResourceBundle::clone() const {
249     return new ResourceBundle(*this);
250 }
251 
getString(UErrorCode & status) const252 UnicodeString ResourceBundle::getString(UErrorCode& status) const {
253     int32_t len = 0;
254     const char16_t *r = ures_getString(fResource, &len, &status);
255     return UnicodeString(true, r, len);
256 }
257 
getBinary(int32_t & len,UErrorCode & status) const258 const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const {
259     return ures_getBinary(fResource, &len, &status);
260 }
261 
getIntVector(int32_t & len,UErrorCode & status) const262 const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const {
263     return ures_getIntVector(fResource, &len, &status);
264 }
265 
getUInt(UErrorCode & status) const266 uint32_t ResourceBundle::getUInt(UErrorCode& status) const {
267     return ures_getUInt(fResource, &status);
268 }
269 
getInt(UErrorCode & status) const270 int32_t ResourceBundle::getInt(UErrorCode& status) const {
271     return ures_getInt(fResource, &status);
272 }
273 
getName() const274 const char *ResourceBundle::getName() const {
275     return ures_getName(fResource);
276 }
277 
getKey() const278 const char *ResourceBundle::getKey() const {
279     return ures_getKey(fResource);
280 }
281 
getType() const282 UResType ResourceBundle::getType() const {
283     return ures_getType(fResource);
284 }
285 
getSize() const286 int32_t ResourceBundle::getSize() const {
287     return ures_getSize(fResource);
288 }
289 
hasNext() const290 UBool ResourceBundle::hasNext() const {
291     return ures_hasNext(fResource);
292 }
293 
resetIterator()294 void ResourceBundle::resetIterator() {
295     ures_resetIterator(fResource);
296 }
297 
getNext(UErrorCode & status)298 ResourceBundle ResourceBundle::getNext(UErrorCode& status) {
299     UResourceBundle r;
300 
301     ures_initStackObject(&r);
302     ures_getNextResource(fResource, &r, &status);
303     ResourceBundle res(&r, status);
304     if (U_SUCCESS(status)) {
305         ures_close(&r);
306     }
307     return res;
308 }
309 
getNextString(UErrorCode & status)310 UnicodeString ResourceBundle::getNextString(UErrorCode& status) {
311     int32_t len = 0;
312     const char16_t* r = ures_getNextString(fResource, &len, nullptr, &status);
313     return UnicodeString(true, r, len);
314 }
315 
getNextString(const char ** key,UErrorCode & status)316 UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) {
317     int32_t len = 0;
318     const char16_t* r = ures_getNextString(fResource, &len, key, &status);
319     return UnicodeString(true, r, len);
320 }
321 
get(int32_t indexR,UErrorCode & status) const322 ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const {
323     UResourceBundle r;
324 
325     ures_initStackObject(&r);
326     ures_getByIndex(fResource, indexR, &r, &status);
327     ResourceBundle res(&r, status);
328     if (U_SUCCESS(status)) {
329         ures_close(&r);
330     }
331     return res;
332 }
333 
getStringEx(int32_t indexS,UErrorCode & status) const334 UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const {
335     int32_t len = 0;
336     const char16_t* r = ures_getStringByIndex(fResource, indexS, &len, &status);
337     return UnicodeString(true, r, len);
338 }
339 
get(const char * key,UErrorCode & status) const340 ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const {
341     UResourceBundle r;
342 
343     ures_initStackObject(&r);
344     ures_getByKey(fResource, key, &r, &status);
345     ResourceBundle res(&r, status);
346     if (U_SUCCESS(status)) {
347         ures_close(&r);
348     }
349     return res;
350 }
351 
getWithFallback(const char * key,UErrorCode & status)352 ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){
353     UResourceBundle r;
354     ures_initStackObject(&r);
355     ures_getByKeyWithFallback(fResource, key, &r, &status);
356     ResourceBundle res(&r, status);
357     if(U_SUCCESS(status)){
358         ures_close(&r);
359     }
360     return res;
361 }
getStringEx(const char * key,UErrorCode & status) const362 UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const {
363     int32_t len = 0;
364     const char16_t* r = ures_getStringByKey(fResource, key, &len, &status);
365     return UnicodeString(true, r, len);
366 }
367 
368 const char*
getVersionNumber() const369 ResourceBundle::getVersionNumber()  const
370 {
371     return ures_getVersionNumberInternal(fResource);
372 }
373 
getVersion(UVersionInfo versionInfo) const374 void ResourceBundle::getVersion(UVersionInfo versionInfo) const {
375     ures_getVersion(fResource, versionInfo);
376 }
377 
getLocale() const378 const Locale &ResourceBundle::getLocale() const {
379     static UMutex gLocaleLock;
380     Mutex lock(&gLocaleLock);
381     if (fLocale != nullptr) {
382         return *fLocale;
383     }
384     UErrorCode status = U_ZERO_ERROR;
385     const char *localeName = ures_getLocaleInternal(fResource, &status);
386     ResourceBundle *ncThis = const_cast<ResourceBundle *>(this);
387     ncThis->fLocale = new Locale(localeName);
388     return ncThis->fLocale != nullptr ? *ncThis->fLocale : Locale::getDefault();
389 }
390 
getLocale(ULocDataLocaleType type,UErrorCode & status) const391 const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const
392 {
393   return ures_getLocaleByType(fResource, type, &status);
394 }
395 
396 U_NAMESPACE_END
397 //eof
398