Home | History | Annotate | Download | only in common
      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 
    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 
    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 
    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 
    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 
    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 
    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 *
    247 ResourceBundle::clone() const {
    248     return new ResourceBundle(*this);
    249 }
    250 
    251 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 
    257 const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const {
    258     return ures_getBinary(fResource, &len, &status);
    259 }
    260 
    261 const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const {
    262     return ures_getIntVector(fResource, &len, &status);
    263 }
    264 
    265 uint32_t ResourceBundle::getUInt(UErrorCode& status) const {
    266     return ures_getUInt(fResource, &status);
    267 }
    268 
    269 int32_t ResourceBundle::getInt(UErrorCode& status) const {
    270     return ures_getInt(fResource, &status);
    271 }
    272 
    273 const char *ResourceBundle::getName(void) const {
    274     return ures_getName(fResource);
    275 }
    276 
    277 const char *ResourceBundle::getKey(void) const {
    278     return ures_getKey(fResource);
    279 }
    280 
    281 UResType ResourceBundle::getType(void) const {
    282     return ures_getType(fResource);
    283 }
    284 
    285 int32_t ResourceBundle::getSize(void) const {
    286     return ures_getSize(fResource);
    287 }
    288 
    289 UBool ResourceBundle::hasNext(void) const {
    290     return ures_hasNext(fResource);
    291 }
    292 
    293 void ResourceBundle::resetIterator(void) {
    294     ures_resetIterator(fResource);
    295 }
    296 
    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 
    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 
    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 
    321 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 
    333 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 
    339 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 
    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 }
    361 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*
    368 ResourceBundle::getVersionNumber()  const
    369 {
    370     return ures_getVersionNumberInternal(fResource);
    371 }
    372 
    373 void ResourceBundle::getVersion(UVersionInfo versionInfo) const {
    374     ures_getVersion(fResource, versionInfo);
    375 }
    376 
    377 static UMutex gLocaleLock = U_MUTEX_INITIALIZER;
    378 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 
    390 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