]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/resbund.cpp
ICU-3.13.tar.gz
[apple/icu.git] / icuSources / common / resbund.cpp
CommitLineData
b75a7d8f
A
1/*
2**********************************************************************
3* Copyright (C) 1997-2003, 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 "uresimp.h"
53
54U_NAMESPACE_BEGIN
55
56/*-----------------------------------------------------------------------------
57 * Implementation Notes
58 *
59 * Resource bundles are read in once, and thereafter cached.
60 * ResourceBundle statically keeps track of which files have been
61 * read, so we are guaranteed that each file is read at most once.
62 * Resource bundles can be loaded from different data directories and
63 * will be treated as distinct, even if they are for the same locale.
64 *
65 * Resource bundles are lightweight objects, which have pointers to
66 * one or more shared Hashtable objects containing all the data.
67 * Copying would be cheap, but there is no copy constructor, since
68 * there wasn't one in the original API.
69 *
70 * The ResourceBundle parsing mechanism is implemented as a transition
71 * network, for easy maintenance and modification. The network is
72 * implemented as a matrix (instead of in code) to make this even
73 * easier. The matrix contains Transition objects. Each Transition
74 * object describes a destination node and an action to take before
75 * moving to the destination node. The source node is encoded by the
76 * index of the object in the array that contains it. The pieces
77 * needed to understand the transition network are the enums for node
78 * IDs and actions, the parse() method, which walks through the
79 * network and implements the actions, and the network itself. The
80 * network guarantees certain conditions, for example, that a new
81 * resource will not be closed until one has been opened first; or
82 * that data will not be stored into a TaggedList until a TaggedList
83 * has been created. Nonetheless, the code in parse() does some
84 * consistency checks as it runs the network, and fails with an
85 * U_INTERNAL_PROGRAM_ERROR if one of these checks fails. If the input
86 * data has a bad format, an U_INVALID_FORMAT_ERROR is returned. If you
87 * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in
88 * it.
89 *
90 * Old functionality of multiple locales in a single file is still
91 * supported. For this reason, LOCALE names override FILE names. If
92 * data for en_US is located in the en.txt file, once it is loaded,
93 * the code will not care where it came from (other than remembering
94 * which directory it came from). However, if there is an en_US
95 * resource in en_US.txt, that will take precedence. There is no
96 * limit to the number or type of resources that can be stored in a
97 * file, however, files are only searched in a specific way. If
98 * en_US_CA is requested, then first en_US_CA.txt is searched, then
99 * en_US.txt, then en.txt, then default.txt. So it only makes sense
100 * to put certain locales in certain files. In this example, it would
101 * be logical to put en_US_CA, en_US, and en into the en.txt file,
102 * since they would be found there if asked for. The extreme example
103 * is to place all locale resources into default.txt, which should
104 * also work.
105 *
106 * Inheritance is implemented. For example, xx_YY_zz inherits as
107 * follows: xx_YY_zz, xx_YY, xx, default. Inheritance is implemented
108 * as an array of hashtables. There will be from 1 to 4 hashtables in
109 * the array.
110 *
111 * Fallback files are implemented. The fallback pattern is Language
112 * Country Variant (LCV) -> LC -> L. Fallback is first done for the
113 * requested locale. Then it is done for the default locale, as
114 * returned by Locale::getDefault(). Then the special file
115 * default.txt is searched for the default locale. The overall FILE
116 * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default.
117 *
118 * Note that although file name searching includes the default locale,
119 * once a ResourceBundle object is constructed, the inheritance path
120 * no longer includes the default locale. The path is LCV -> LC -> L
121 * -> default.
122 *
123 * File parsing is lazy. Nothing is parsed unless it is called for by
124 * someone. So when a ResourceBundle for xx_YY_zz is constructed,
125 * only that locale is parsed (along with anything else in the same
126 * file). Later, if the FooBar tag is asked for, and if it isn't
127 * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and
128 * so forth, until the chain is exhausted or the tag is found.
129 *
130 * Thread-safety is implemented around caches, both the cache that
131 * stores all the resouce data, and the cache that stores flags
132 * indicating whether or not a file has been visited. These caches
133 * delete their storage at static cleanup time, when the process
134 * quits.
135 *
136 * ResourceBundle supports TableCollation as a special case. This
137 * involves having special ResourceBundle objects which DO own their
138 * data, since we don't want large collation rule strings in the
139 * ResourceBundle cache (these are already cached in the
140 * TableCollation cache). TableCollation files (.ctx files) have the
141 * same format as normal resource data files, with a different
142 * interpretation, from the standpoint of ResourceBundle. .ctx files
143 * are loaded into otherwise ordinary ResourceBundle objects. They
144 * don't inherit (that's implemented by TableCollation) and they own
145 * their data (as mentioned above). However, they still support
146 * possible multiple locales in a single .ctx file. (This is in
147 * practice a bad idea, since you only want the one locale you're
148 * looking for, and only one tag will be present
149 * ("CollationElements"), so you don't need an inheritance chain of
150 * multiple locales.) Up to 4 locale resources will be loaded from a
151 * .ctx file; everything after the first 4 is ignored (parsed and
152 * deleted). (Normal .txt files have no limit.) Instead of being
153 * loaded into the cache, and then looked up as needed, the locale
154 * resources are read straight into the ResourceBundle object.
155 *
156 * The Index, which used to reside in default.txt, has been moved to a
157 * new file, index.txt. This file contains a slightly modified format
158 * with the addition of the "InstalledLocales" tag; it looks like:
159 *
160 * Index {
161 * InstalledLocales {
162 * ar
163 * ..
164 * zh_TW
165 * }
166 * }
167 */
168//-----------------------------------------------------------------------------
169
170const char ResourceBundle::fgClassID=0;
171
172ResourceBundle::ResourceBundle( const UnicodeString& path,
173 const Locale& locale,
174 UErrorCode& error)
175 :UObject(), locName(NULL)
176{
177 constructForLocale(path, locale, error);
178}
179
180ResourceBundle::ResourceBundle(UErrorCode &err)
181 :UObject(), locName(NULL)
182{
183 resource = ures_open(0, Locale::getDefault().getName(), &err);
184}
185
186ResourceBundle::ResourceBundle( const UnicodeString& path,
187 UErrorCode& error)
188 :UObject(), locName(NULL)
189{
190 constructForLocale(path, Locale::getDefault(), error);
191}
192
193ResourceBundle::ResourceBundle(const ResourceBundle &other)
194 :UObject(other), locName(NULL)
195{
196 UErrorCode status = U_ZERO_ERROR;
197
198 if (other.resource) {
199 resource = ures_copyResb(0, other.resource, &status);
200 } else {
201 /* Copying a bad resource bundle */
202 resource = NULL;
203 }
204}
205
206ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err)
207 :UObject(), locName(NULL)
208{
209 if (res) {
210 resource = ures_copyResb(0, res, &err);
211 } else {
212 /* Copying a bad resource bundle */
213 resource = NULL;
214 }
215}
216
217ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err)
218 :UObject(), locName(NULL)
219{
220 resource = ures_open(path, locale.getName(), &err);
221}
222
223
224ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other)
225{
226 if(this == &other) {
227 return *this;
228 }
229 if(resource != 0) {
230 ures_close(resource);
231 resource = NULL;
232 }
233 UErrorCode status = U_ZERO_ERROR;
234 if (other.resource) {
235 resource = ures_copyResb(0, other.resource, &status);
236 } else {
237 /* Copying a bad resource bundle */
238 resource = NULL;
239 }
240 return *this;
241}
242
243ResourceBundle::~ResourceBundle()
244{
245 if(resource != 0) {
246 ures_close(resource);
247 }
248 if(locName != NULL) {
249 delete(locName);
250 }
251}
252
253void
254ResourceBundle::constructForLocale(const UnicodeString& path,
255 const Locale& locale,
256 UErrorCode& error)
257{
258 char name[300];
259
260 if(!path.isEmpty()) {
261 path.extract(name, sizeof(name), 0, error);
262 resource = ures_open(name, locale.getName(), &error);
263 } else {
264 resource = ures_open(0, locale.getName(), &error);
265 }
266}
267
268UnicodeString ResourceBundle::getString(UErrorCode& status) const {
269 int32_t len = 0;
270 const UChar *r = ures_getString(resource, &len, &status);
271 return UnicodeString(TRUE, r, len);
272}
273
274const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const {
275 return ures_getBinary(resource, &len, &status);
276}
277
278const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const {
279 return ures_getIntVector(resource, &len, &status);
280}
281
282uint32_t ResourceBundle::getUInt(UErrorCode& status) const {
283 return ures_getUInt(resource, &status);
284}
285
286int32_t ResourceBundle::getInt(UErrorCode& status) const {
287 return ures_getInt(resource, &status);
288}
289
290const char *ResourceBundle::getName(void) {
291 return ures_getName(resource);
292}
293
294const char *ResourceBundle::getKey(void) {
295 return ures_getKey(resource);
296}
297
298UResType ResourceBundle::getType(void) {
299 return ures_getType(resource);
300}
301
302int32_t ResourceBundle::getSize(void) const {
303 return ures_getSize(resource);
304}
305
306UBool ResourceBundle::hasNext(void) const {
307 return ures_hasNext(resource);
308}
309
310void ResourceBundle::resetIterator(void) {
311 ures_resetIterator(resource);
312}
313
314ResourceBundle ResourceBundle::getNext(UErrorCode& status) {
315 UResourceBundle r;
316
317 ures_initStackObject(&r);
318 ures_getNextResource(resource, &r, &status);
319 ResourceBundle res(&r, status);
320 if (U_SUCCESS(status)) {
321 ures_close(&r);
322 }
323 return res;
324}
325
326UnicodeString ResourceBundle::getNextString(UErrorCode& status) {
327 int32_t len = 0;
328 const UChar* r = ures_getNextString(resource, &len, 0, &status);
329 return UnicodeString(TRUE, r, len);
330}
331
332UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) {
333 int32_t len = 0;
334 const UChar* r = ures_getNextString(resource, &len, key, &status);
335 return UnicodeString(TRUE, r, len);
336}
337
338ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const {
339 UResourceBundle r;
340
341 ures_initStackObject(&r);
342 ures_getByIndex(resource, indexR, &r, &status);
343 ResourceBundle res(&r, status);
344 if (U_SUCCESS(status)) {
345 ures_close(&r);
346 }
347 return res;
348}
349
350UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const {
351 int32_t len = 0;
352 const UChar* r = ures_getStringByIndex(resource, indexS, &len, &status);
353 return UnicodeString(TRUE, r, len);
354}
355
356ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const {
357 UResourceBundle r;
358
359 ures_initStackObject(&r);
360 ures_getByKey(resource, key, &r, &status);
361 ResourceBundle res(&r, status);
362 if (U_SUCCESS(status)) {
363 ures_close(&r);
364 }
365 return res;
366}
367
368UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const {
369 int32_t len = 0;
370 const UChar* r = ures_getStringByKey(resource, key, &len, &status);
371 return UnicodeString(TRUE, r, len);
372}
373
374const char*
375ResourceBundle::getVersionNumber() const
376{
377 return ures_getVersionNumber(resource);
378}
379
380void ResourceBundle::getVersion(UVersionInfo versionInfo) const {
381 ures_getVersion(resource, versionInfo);
382}
383
384const Locale &ResourceBundle::getLocale(void) const
385{
386 if(locName == NULL) {
387 UErrorCode status = U_ZERO_ERROR;
388 const char *localeName = ures_getLocale(resource, &status);
389 ResourceBundle *me = (ResourceBundle *)this; // semantically const
390 me->locName = new Locale(localeName);
391 }
392 return *locName;
393}
394
395//eof
396U_NAMESPACE_END