resbund.cpp (15242B)
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 :UObject(), fLocale(nullptr) 181 { 182 fResource = ures_open(nullptr, Locale::getDefault().getName(), &err); 183 } 184 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 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 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 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 239 ResourceBundle::~ResourceBundle() 240 { 241 if (fResource != nullptr) { 242 ures_close(fResource); 243 } 244 delete fLocale; 245 } 246 247 ResourceBundle * 248 ResourceBundle::clone() const { 249 return new ResourceBundle(*this); 250 } 251 252 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 258 const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const { 259 return ures_getBinary(fResource, &len, &status); 260 } 261 262 const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const { 263 return ures_getIntVector(fResource, &len, &status); 264 } 265 266 uint32_t ResourceBundle::getUInt(UErrorCode& status) const { 267 return ures_getUInt(fResource, &status); 268 } 269 270 int32_t ResourceBundle::getInt(UErrorCode& status) const { 271 return ures_getInt(fResource, &status); 272 } 273 274 const char *ResourceBundle::getName() const { 275 return ures_getName(fResource); 276 } 277 278 const char *ResourceBundle::getKey() const { 279 return ures_getKey(fResource); 280 } 281 282 UResType ResourceBundle::getType() const { 283 return ures_getType(fResource); 284 } 285 286 int32_t ResourceBundle::getSize() const { 287 return ures_getSize(fResource); 288 } 289 290 UBool ResourceBundle::hasNext() const { 291 return ures_hasNext(fResource); 292 } 293 294 void ResourceBundle::resetIterator() { 295 ures_resetIterator(fResource); 296 } 297 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 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 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 322 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 334 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 340 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 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 } 362 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* 369 ResourceBundle::getVersionNumber() const 370 { 371 return ures_getVersionNumberInternal(fResource); 372 } 373 374 void ResourceBundle::getVersion(UVersionInfo versionInfo) const { 375 ures_getVersion(fResource, versionInfo); 376 } 377 378 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 391 Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const 392 { 393 return ures_getLocaleByType(fResource, type, &status); 394 } 395 396 U_NAMESPACE_END 397 //eof