home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / common / resbund.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-19  |  43.0 KB  |  1,337 lines

  1. /*
  2. *******************************************************************************
  3. *                                                                             *
  4. * COPYRIGHT:                                                                  *
  5. *   (C) Copyright Taligent, Inc.,  1997                                       *
  6. *   (C) Copyright International Business Machines Corporation,  1997-1999     *
  7. *   Licensed Material - Program-Property of IBM - All Rights Reserved.        *
  8. *   US Government Users Restricted Rights - Use, duplication, or disclosure   *
  9. *   restricted by GSA ADP Schedule Contract with IBM Corp.                    *
  10. *                                                                             *
  11. *******************************************************************************
  12. *
  13. * File resbund.cpp
  14. *
  15. * Modification History:
  16. *
  17. *   Date        Name        Description
  18. *   02/05/97    aliu        Fixed bug in chopLocale.  Added scanForLocaleInFile
  19. *                           based on code taken from scanForLocale.  Added
  20. *                           constructor which attempts to read resource bundle
  21. *                           from a specific file, without searching other files.
  22. *   02/11/97    aliu        Added UErrorCode return values to constructors.  Fixed
  23. *                           infinite loops in scanForFile and scanForLocale.
  24. *                           Modified getRawResourceData to not delete storage in
  25. *                           localeData and resourceData which it doesn't own.
  26. *                           Added Mac compatibility #ifdefs for tellp() and
  27. *                           ios::nocreate.
  28. *   03/04/97    aliu        Modified to use ExpandingDataSink objects instead of
  29. *                           the highly inefficient ostrstream objects.
  30. *   03/13/97    aliu        Rewrote to load in entire resource bundle and store
  31. *                           it as a Hashtable of ResourceBundleData objects.
  32. *                           Added state table to govern parsing of files.
  33. *                           Modified to load locale index out of new file distinct
  34. *                           from default.txt.
  35. *   03/25/97    aliu        Modified to support 2-d arrays, needed for timezone data.
  36. *                           Added support for custom file suffixes.  Again, needed to
  37. *                           support timezone data.  Improved error handling to detect
  38. *                           duplicate tags and subtags.
  39. *   04/07/97    aliu        Fixed bug in getHashtableForLocale().  Fixed handling of
  40. *                           failing UErrorCode values on entry to API methods.
  41. *                           Fixed bugs in getArrayItem() for negative indices.
  42. *   04/29/97    aliu        Update to use new Hashtable deletion protocol.
  43. *   05/06/97    aliu        Flattened kTransitionTable for HP compiler.  Fixed usage of
  44. *                           CharString.
  45. * 06/11/99      stephen     Removed parsing of .txt files.
  46. *                           Reworked to use new binary format.
  47. *                           Cleaned up.
  48. * 06/14/99      stephen     Removed methods taking a filename suffix.
  49. * 06/22/99      stephen     Added missing T_FileStream_close in parse()
  50. *******************************************************************************
  51. */
  52.  
  53. #include "rbcache.h"
  54.  
  55. #include "resbund.h"
  56. #include "mutex.h"
  57.  
  58. #include "unistrm.h"
  59. #include "filestrm.h"
  60. #include "cstring.h"
  61. #include "uhash.h"
  62.  
  63. #include "rbdata.h"
  64. #include "rbread.h"
  65.  
  66. #include <iostream.h>
  67. #include <string.h>
  68. #include <wchar.h>
  69.  
  70. /*-----------------------------------------------------------------------------
  71.  * Implementation Notes
  72.  *
  73.  * Resource bundles are read in once, and thereafter cached.
  74.  * ResourceBundle statically keeps track of which files have been
  75.  * read, so we are guaranteed that each file is read at most once.
  76.  * Resource bundles can be loaded from different data directories and
  77.  * will be treated as distinct, even if they are for the same locale.
  78.  *
  79.  * Resource bundles are lightweight objects, which have pointers to
  80.  * one or more shared Hashtable objects containing all the data.
  81.  * Copying would be cheap, but there is no copy constructor, since
  82.  * there wasn't one in the original API.
  83.  *
  84.  * The ResourceBundle parsing mechanism is implemented as a transition
  85.  * network, for easy maintenance and modification.  The network is
  86.  * implemented as a matrix (instead of in code) to make this even
  87.  * easier.  The matrix contains Transition objects.  Each Transition
  88.  * object describes a destination node and an action to take before
  89.  * moving to the destination node.  The source node is encoded by the
  90.  * index of the object in the array that contains it.  The pieces
  91.  * needed to understand the transition network are the enums for node
  92.  * IDs and actions, the parse() method, which walks through the
  93.  * network and implements the actions, and the network itself.  The
  94.  * network guarantees certain conditions, for example, that a new
  95.  * resource will not be closed until one has been opened first; or
  96.  * that data will not be stored into a TaggedList until a TaggedList
  97.  * has been created.  Nonetheless, the code in parse() does some
  98.  * consistency checks as it runs the network, and fails with an
  99.  * U_INTERNAL_PROGRAM_ERROR if one of these checks fails.  If the input
  100.  * data has a bad format, an U_INVALID_FORMAT_ERROR is returned.  If you
  101.  * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in
  102.  * it.
  103.  *
  104.  * Old functionality of multiple locales in a single file is still
  105.  * supported.  For this reason, LOCALE names override FILE names.  If
  106.  * data for en_US is located in the en.txt file, once it is loaded,
  107.  * the code will not care where it came from (other than remembering
  108.  * which directory it came from).  However, if there is an en_US
  109.  * resource in en_US.txt, that will take precedence.  There is no
  110.  * limit to the number or type of resources that can be stored in a
  111.  * file, however, files are only searched in a specific way.  If
  112.  * en_US_CA is requested, then first en_US_CA.txt is searched, then
  113.  * en_US.txt, then en.txt, then default.txt.  So it only makes sense
  114.  * to put certain locales in certain files.  In this example, it would
  115.  * be logical to put en_US_CA, en_US, and en into the en.txt file,
  116.  * since they would be found there if asked for.  The extreme example
  117.  * is to place all locale resources into default.txt, which should
  118.  * also work.
  119.  *
  120.  * Inheritance is implemented.  For example, xx_YY_zz inherits as
  121.  * follows: xx_YY_zz, xx_YY, xx, default.  Inheritance is implemented
  122.  * as an array of hashtables.  There will be from 1 to 4 hashtables in
  123.  * the array.
  124.  *
  125.  * Fallback files are implemented.  The fallback pattern is Language
  126.  * Country Variant (LCV) -> LC -> L.  Fallback is first done for the
  127.  * requested locale.  Then it is done for the default locale, as
  128.  * returned by Locale::getDefault().  Then the special file
  129.  * default.txt is searched for the default locale.  The overall FILE
  130.  * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default.
  131.  *
  132.  * Note that although file name searching includes the default locale,
  133.  * once a ResourceBundle object is constructed, the inheritance path
  134.  * no longer includes the default locale.  The path is LCV -> LC -> L
  135.  * -> default.
  136.  *
  137.  * File parsing is lazy.  Nothing is parsed unless it is called for by
  138.  * someone.  So when a ResourceBundle for xx_YY_zz is constructed,
  139.  * only that locale is parsed (along with anything else in the same
  140.  * file).  Later, if the FooBar tag is asked for, and if it isn't
  141.  * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and
  142.  * so forth, until the chain is exhausted or the tag is found.
  143.  *
  144.  * Thread-safety is implemented around caches, both the cache that
  145.  * stores all the resouce data, and the cache that stores flags
  146.  * indicating whether or not a file has been visited.  These caches
  147.  * delete their storage at static cleanup time, when the process
  148.  * quits.
  149.  *
  150.  * ResourceBundle supports TableCollation as a special case.  This
  151.  * involves having special ResourceBundle objects which DO own their
  152.  * data, since we don't want large collation rule strings in the
  153.  * ResourceBundle cache (these are already cached in the
  154.  * TableCollation cache).  TableCollation files (.ctx files) have the
  155.  * same format as normal resource data files, with a different
  156.  * interpretation, from the standpoint of ResourceBundle.  .ctx files
  157.  * are loaded into otherwise ordinary ResourceBundle objects.  They
  158.  * don't inherit (that's implemented by TableCollation) and they own
  159.  * their data (as mentioned above).  However, they still support
  160.  * possible multiple locales in a single .ctx file.  (This is in
  161.  * practice a bad idea, since you only want the one locale you're
  162.  * looking for, and only one tag will be present
  163.  * ("CollationElements"), so you don't need an inheritance chain of
  164.  * multiple locales.)  Up to 4 locale resources will be loaded from a
  165.  * .ctx file; everything after the first 4 is ignored (parsed and
  166.  * deleted).  (Normal .txt files have no limit.)  Instead of being
  167.  * loaded into the cache, and then looked up as needed, the locale
  168.  * resources are read straight into the ResourceBundle object.
  169.  *
  170.  * The Index, which used to reside in default.txt, has been moved to a
  171.  * new file, index.txt.  This file contains a slightly modified format
  172.  * with the addition of the "InstalledLocales" tag; it looks like:
  173.  *
  174.  * Index {
  175.  *   InstalledLocales {
  176.  *     ar
  177.  *     ..
  178.  *     zh_TW
  179.  *   }
  180.  * }
  181.  */
  182. //-----------------------------------------------------------------------------
  183.  
  184. const char* ResourceBundle::kDefaultSuffix      = ".res";
  185. const int32_t ResourceBundle::kDefaultSuffixLen = 4;
  186. const char* ResourceBundle::kDefaultFilename    = "default";
  187. const char* ResourceBundle::kDefaultLocaleName  = "default";
  188. const char* ResourceBundle::kIndexLocaleName    = "index";
  189. const char* ResourceBundle::kIndexFilename      = "index";
  190. const char* ResourceBundle::kIndexTag           = "InstalledLocales";
  191.  
  192. // The default minor version and the version separator must be exactly one
  193. // character long.
  194. const char*         ResourceBundle::kDefaultMinorVersion    = "0";
  195. const char*         ResourceBundle::kVersionSeparator       = ".";
  196. const UnicodeString ResourceBundle::kVersionTag("Version");
  197.  
  198. ResourceBundleCache*    ResourceBundle::fgUserCache = new ResourceBundleCache();
  199. VisitedFileCache*       ResourceBundle::fgUserVisitedFiles = new VisitedFileCache();
  200. // allocated on the heap so we don't have to expose the definitions of
  201. // these classes to the world
  202.  
  203. //-----------------------------------------------------------------------------
  204.  
  205. ResourceBundle::LocaleFallbackIterator::LocaleFallbackIterator(const UnicodeString& startingLocale,
  206.                                    const UnicodeString& root,
  207.                                    bool_t useDefaultLocale)
  208. :   fLocale(startingLocale),
  209.     fRoot(root),
  210.     fUseDefaultLocale(useDefaultLocale),
  211.     fTriedDefaultLocale(FALSE),
  212.     fTriedRoot(FALSE)
  213. {
  214.     if (fUseDefaultLocale) Locale::getDefault().getName(fDefaultLocale);
  215. }
  216.  
  217. bool_t
  218. ResourceBundle::LocaleFallbackIterator::nextLocale(UErrorCode& status)
  219. {
  220.   if(fUseDefaultLocale)
  221.     fTriedDefaultLocale = fTriedDefaultLocale || (fLocale == fDefaultLocale);
  222.   
  223.   chopLocale();
  224.   if(status != U_USING_DEFAULT_ERROR) 
  225.     status = U_USING_FALLBACK_ERROR;
  226.   
  227.   if(fLocale.size() == 0) {
  228.     if(fUseDefaultLocale && !fTriedDefaultLocale) {
  229.       fLocale = fDefaultLocale;
  230.       fTriedDefaultLocale = TRUE;
  231.       status = U_USING_DEFAULT_ERROR;
  232.     }
  233.     else if( ! fTriedRoot) {
  234.       fLocale = fRoot;
  235.       fTriedRoot = TRUE;
  236.       status = U_USING_DEFAULT_ERROR;
  237.     }
  238.     else {
  239.       status = U_MISSING_RESOURCE_ERROR;
  240.       return FALSE;
  241.     }
  242.   }
  243.   
  244.   //  cerr << "* " << fLocale << " " << errorName(status) << endl;
  245.   return TRUE;
  246. }
  247.  
  248. void
  249. ResourceBundle::LocaleFallbackIterator::chopLocale()
  250. {
  251.   int32_t size = fLocale.size();
  252.   int32_t i;
  253.   
  254.   for(i = size - 1; i > 0; i--) 
  255.     if(fLocale[i] == 0x005F/*'_'*/) 
  256.       break;
  257.   
  258.   if(i < 0) 
  259.     i = 0;
  260.   
  261.   fLocale.remove(i, size - i);
  262. }
  263.  
  264. //-----------------------------------------------------------------------------
  265.  
  266. ResourceBundle::ResourceBundle( const UnicodeString&    path,
  267.                                 const Locale&           locale,
  268.                                 UErrorCode&              error)
  269.   : fgCache(fgUserCache),
  270.     fgVisitedFiles(fgUserVisitedFiles)
  271. {
  272.   constructForLocale(PathInfo(path, kDefaultSuffix), locale, error);
  273. }
  274.  
  275. ResourceBundle::ResourceBundle( const UnicodeString&    path,
  276.                                 UErrorCode&              error)
  277.   : fgCache(fgUserCache),
  278.     fgVisitedFiles(fgUserVisitedFiles)
  279. {
  280.   constructForLocale(PathInfo(path, kDefaultSuffix), 
  281.              Locale::getDefault(), error);
  282. }
  283.  
  284. /**
  285.  * This constructor is used by TableCollation to load a resource
  286.  * bundle from a specific file, without trying other files.  This is
  287.  * used by the TableCollation caching mechanism.  This is not a public
  288.  * API constructor.  
  289.  */
  290. ResourceBundle::ResourceBundle( const UnicodeString&    path,
  291.                                 const UnicodeString&    localeName,
  292.                                 UErrorCode&              status)
  293.   :   fPath(path, kDefaultSuffix),
  294.       fRealLocaleID(localeName),
  295.       fIsDataOwned(TRUE),
  296.       fVersionID(0),
  297.       fgCache(fgUserCache),
  298.       fgVisitedFiles(fgUserVisitedFiles)
  299. {
  300.   status = U_ZERO_ERROR;
  301.   
  302.   int32_t i;
  303.   for(i = 0; i < kDataCount; ++i) {
  304.     fData[i] = 0;
  305.     fLoaded[i] = FALSE;
  306.     fDataStatus[i] = U_INTERNAL_PROGRAM_ERROR;
  307.   }
  308.   
  309.   fLocaleIterator = 0;
  310.   
  311.   // If the file doesn't exist, return an error
  312.   if(fPath.fileExists(localeName)) {
  313.     parse(fPath, localeName, saveCollationHashtable, 
  314.       (void*)this, fgCache, status);
  315.   }
  316.   else {
  317.     status = U_MISSING_RESOURCE_ERROR;
  318.   }
  319.   
  320.   // Prevent further attempts to load hashtables
  321.   for(i = 0; i < kDataCount; ++i) 
  322.     fLoaded[i] = TRUE;
  323. }
  324.  
  325. void
  326. ResourceBundle::saveCollationHashtable(const UnicodeString& localeName,
  327.                        UHashtable* hashtable,
  328.                        void* context,
  329.                        ResourceBundleCache* fgCache)
  330. {
  331.   ResourceBundle* bundle = (ResourceBundle*)context;
  332.   for(int32_t i = 0; i < kDataCount; ++i) {
  333.     if( ! bundle->fLoaded[i]) {
  334.       bundle->fData[i] = hashtable;
  335.       bundle->fLoaded[i] = TRUE;
  336.       bundle->fDataStatus[i] = U_ZERO_ERROR; /* ??? */
  337.       return;
  338.     }
  339.   }
  340.   // Out of room; discard extra data.  We only expect to see one anyway.
  341.   uhash_close(hashtable);
  342. }
  343.  
  344. ResourceBundle::ResourceBundle(const wchar_t* path,
  345.                    const Locale& locale, 
  346.                    UErrorCode& err)
  347.   : fgCache(fgUserCache),
  348.     fgVisitedFiles(fgUserVisitedFiles)
  349. {
  350.   int32_t wideNameLen = icu_mbstowcs(NULL, kDefaultSuffix, kDefaultSuffixLen);
  351.   wchar_t* wideName = new wchar_t[wideNameLen + 1];
  352.   icu_mbstowcs(wideName, kDefaultSuffix, kDefaultSuffixLen);
  353.   wideName[wideNameLen] = 0;
  354.   constructForLocale(PathInfo(path, wideName), locale, err);
  355.   delete [] wideName;
  356. }
  357.  
  358. ResourceBundle::~ResourceBundle()
  359. {
  360.   delete fLocaleIterator;
  361.   delete [] fVersionID;
  362.   
  363.   if(fIsDataOwned)
  364.     for(int32_t i = 0; i < kDataCount; ++i) {
  365.       if(fData[i]) 
  366.     uhash_close((UHashtable*)fData[i]);
  367.     }
  368. }
  369.  
  370. void 
  371. ResourceBundle::constructForLocale(const PathInfo& path,
  372.                    const Locale& locale,
  373.                    UErrorCode& error)
  374. {
  375.   int32_t i;
  376.   fPath = path;
  377.   fIsDataOwned = FALSE;
  378.   fVersionID = 0;
  379.   
  380.   error = U_ZERO_ERROR;
  381.   
  382.   locale.getName(fRealLocaleID);
  383.   
  384.   // if the locale we were passed is Locale("", "", ""), that, by
  385.   // convention, refers to the root locale (default.txt), even when
  386.   // the system default locale is something else (otherwise there's no
  387.   // way to get to it).  We can accomplish this by changing the locale
  388.   // name to "default" here.  I'm not sure this is the best way to do
  389.   // this, but it's simple and it works.
  390.   if(fRealLocaleID.size() == 0)
  391.     fRealLocaleID = kDefaultLocaleName;
  392.   
  393.   for(i = 1; i < kDataCount; ++i) {
  394.     fData[i] = 0;
  395.     fDataStatus[i] = U_INTERNAL_PROGRAM_ERROR;
  396.     fLoaded[i] = FALSE;
  397.   }
  398.   
  399.   UnicodeString returnedLocale;
  400.   error = U_ZERO_ERROR;
  401.   fData[0] = getHashtableForLocale(fRealLocaleID, returnedLocale, error);
  402.   fLoaded[0] = TRUE;
  403.   fDataStatus[0] = U_ZERO_ERROR;
  404.   if(U_SUCCESS(error)) 
  405.     fRealLocaleID = returnedLocale;
  406.   
  407.   fLocaleIterator = new LocaleFallbackIterator(fRealLocaleID, 
  408.                            kDefaultLocaleName, FALSE);
  409. }
  410.  
  411. /**
  412.  * Return the hash table with data for the given locale.  This method employs
  413.  * fallback both in files and in locale names.  It returns the locale name
  414.  * which is actually used to return the data, if any.
  415.  *
  416.  * Parse all files found at the given path for the given path, in an effort
  417.  * to find data for the given locale.  Use fallbacks and defaults as needed.
  418.  * Store read in file data in the cache for future use.  Return the hashtable
  419.  * for the given locale, if found, or 0 if not.
  420.  */
  421. const UHashtable* 
  422. ResourceBundle::getHashtableForLocale(const UnicodeString& desiredLocale,
  423.                       UnicodeString& returnedLocale,
  424.                       UErrorCode& error)
  425. {
  426.   if(U_FAILURE(error)) return 0;
  427.  
  428.   error = U_ZERO_ERROR;
  429.   const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
  430.   if(h != 0) {
  431.     returnedLocale = desiredLocale;
  432.     return h;
  433.   }
  434.   
  435.   LocaleFallbackIterator iterator(desiredLocale, kDefaultFilename, TRUE);
  436.   bool_t didTryCacheWithFallback = FALSE;
  437.   
  438.   // A note on fileError.  We are tracking two different error states
  439.   // here.  One is that returned while iterating over different files.
  440.   // For instance, when going from de_CH.txt to de.txt we will get a
  441.   // U_USING_FALLBACK_ERROR, but we don't care -- because if de.txt
  442.   // contains the de_CH locale, it isn't a fallback, from our
  443.   // perspective.  Therefore we keep file associated errors in
  444.   // fileError, apart from the error parameter.
  445.   UErrorCode fileError = U_ZERO_ERROR;
  446.  
  447.   for(;;) {
  448.     // Build a filename for the locale.
  449.     if(parseIfUnparsed(fPath, iterator.getLocale(), 
  450.                fgCache, fgVisitedFiles, error)) {
  451.       if(U_FAILURE(error)) 
  452.     return 0;
  453.       
  454.       error = U_ZERO_ERROR;
  455.       h = getFromCacheWithFallback(fPath, desiredLocale, 
  456.                    returnedLocale, fgCache, error);
  457.       didTryCacheWithFallback = TRUE;
  458.       if(h != 0 && U_SUCCESS(error)) 
  459.     return h;
  460.     }
  461.     
  462.     if(!iterator.nextLocale(fileError)) {
  463.       error = U_MISSING_RESOURCE_ERROR;
  464.       break;
  465.     }
  466.   }
  467.   
  468.   // We want to try loading from the cache will fallback at least
  469.   // once.  These lines of code handle the case in which all of the
  470.   // fallback FILES have been loaded, so fgVisitedFiles keeps us from
  471.   // parsing them again.  In this case we still want to make an
  472.   // attempt to load our locale from the cache.
  473.   if(didTryCacheWithFallback) 
  474.     return 0;
  475.   error = U_ZERO_ERROR;
  476.   return getFromCacheWithFallback(fPath, desiredLocale, 
  477.                   returnedLocale, fgCache, error);
  478. }
  479.  
  480. /**
  481.  * Return the hash table with data for the given locale.  This method employs
  482.  * fallback in file names only.  If data is returned, it will be exactly for
  483.  * the given locale.
  484.  */
  485. const UHashtable* 
  486. ResourceBundle::getHashtableForLocale(const UnicodeString& desiredLocale,
  487.                       UErrorCode& error)
  488. {
  489.   if(U_FAILURE(error)) 
  490.     return 0;
  491.   error = U_ZERO_ERROR;
  492.   
  493.   // First try the cache
  494.   const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
  495.   if(h != 0) 
  496.     return h;
  497.   
  498.   // Now try files
  499.   LocaleFallbackIterator iterator(desiredLocale, kDefaultFilename, FALSE);
  500.   
  501.   for(;;) {
  502.     UErrorCode parseError = U_ZERO_ERROR;
  503.     if(parseIfUnparsed(fPath, iterator.getLocale(), 
  504.                fgCache, fgVisitedFiles, parseError)) {
  505.       if(U_FAILURE(parseError)) {
  506.     error = parseError;
  507.     return 0;
  508.       }
  509.       
  510.       const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
  511.       if(h != 0) 
  512.     return h;
  513.     }
  514.     
  515.     if(!iterator.nextLocale(error)) 
  516.       return 0;
  517.   }
  518. }
  519.  
  520. /**
  521.  * Try to retrieve a locale data hash from the cache, using fallbacks
  522.  * if necessary.  Ultimately we will try to load the data under
  523.  * kDefaultLocaleName.  
  524.  */
  525. const UHashtable* 
  526. ResourceBundle::getFromCacheWithFallback(const PathInfo& path,
  527.                      const UnicodeString& desiredLocale,
  528.                      UnicodeString& returnedLocale,
  529.                      ResourceBundleCache* fgCache,
  530.                      UErrorCode& error)
  531. {
  532.   if(U_FAILURE(error)) 
  533.     return 0;
  534.   error = U_ZERO_ERROR;
  535.   
  536.   LocaleFallbackIterator iterator(desiredLocale, kDefaultLocaleName, TRUE);
  537.   
  538.   for(;;) {
  539.     const UHashtable* h = getFromCache(path, iterator.getLocale(), fgCache);
  540.     if(h != 0) {
  541.       returnedLocale = iterator.getLocale();
  542.       return h;
  543.     }
  544.     
  545.     if(!iterator.nextLocale(error)) 
  546.       return 0;
  547.   }
  548. }
  549.  
  550. /**
  551.  * Parse the given file, if it hasn't been attempted already, and if
  552.  * it actually exists.  Return true if a parse is attempted.  Upon
  553.  * return, if the return value is true, the error code may be set as a
  554.  * result of a parse failure to a failing value.  If the parse was
  555.  * successful, additional entries may have been created in the cache.
  556.  */
  557. bool_t
  558. ResourceBundle::parseIfUnparsed(const PathInfo& path,
  559.                 const UnicodeString& locale,
  560.                 ResourceBundleCache* fgCache,
  561.                 VisitedFileCache* fgVisitedFiles,
  562.                 UErrorCode& error)
  563. {
  564.   UnicodeString key(path.makeCacheKey(locale));
  565.   
  566.   if(!fgVisitedFiles->wasVisited(key) && path.fileExists(locale)) {
  567.     parse(path, locale, addToCache, (void*)&path, fgCache, error);
  568.     { 
  569.       Mutex lock;
  570.       fgVisitedFiles->markAsVisited(key);
  571.     }
  572.     return TRUE;
  573.   }
  574.   return FALSE;
  575. }
  576.  
  577. /**
  578.  * Given a tag, try to retrieve the data for that tag.  This method is
  579.  * semantically const, but may actually modify this object.  All
  580.  * public API methods such as getString() rely on getDataForTag()
  581.  * ultimately.  This method implements inheritance of data between
  582.  * locales.  
  583.  */
  584. const ResourceBundleData* 
  585. ResourceBundle::getDataForTag(const UnicodeString& tag,
  586.                   UErrorCode& err) const
  587. {
  588.   err = U_ZERO_ERROR; /* just to make sure there's no fallback/etc left over */
  589.   // Iterate over the kDataCount hashtables which may be associated with this
  590.   // bundle.  At most we have kDataCount, but we may have as few as one.
  591.   for(int32_t i = 0; i < kDataCount; ++i) {
  592.  
  593.   // First try to load up this hashtable, if it hasn't been loaded yet.
  594.     if(!fLoaded[i] && fData[i] == 0) {
  595.       ResourceBundle* nonconst = (ResourceBundle*)this;
  596.       nonconst->fLoaded[i] = TRUE;
  597.       if(fLocaleIterator->nextLocale(err)) {
  598.     UErrorCode getHashtableStatus = U_ZERO_ERROR;
  599.  
  600.     nonconst->fDataStatus[i] = err;
  601.     nonconst->fData[i] = 
  602.       nonconst->getHashtableForLocale(fLocaleIterator->getLocale(), getHashtableStatus);
  603.       }
  604.     }
  605.  
  606.     
  607.     if(fData[i] != 0) {
  608.       const ResourceBundleData* s = 
  609.     (const ResourceBundleData*)uhash_get(fData[i], 
  610.                          tag.hashCode() & 0x7FFFFFFF);
  611.       if(s != 0) {
  612.     err = fDataStatus[i];  /* restore the error from the original lookup. */
  613.     return s;
  614.       }
  615.     }
  616.   }
  617.   
  618. #ifdef _DEBUG
  619.   //  cerr << "Failed to find tag " << tag << " in " << fPath << fRealLocaleID << fFilenameSuffix << endl;
  620.   //  cerr << *this;
  621. #endif
  622.   err = U_MISSING_RESOURCE_ERROR;
  623.   return 0;
  624. }
  625.  
  626. void
  627. ResourceBundle::getString(  const UnicodeString&    resourceTag,
  628.                             UnicodeString&          theString,
  629.                             UErrorCode&              err) const
  630. {
  631.   if(U_FAILURE(err)) 
  632.     return;
  633.   
  634.   const UnicodeString* temp = getString(resourceTag, err);
  635.   if(U_SUCCESS(err))
  636.     theString = *temp;
  637. }
  638.  
  639. const UnicodeString*
  640. ResourceBundle::getString(  const UnicodeString&    resourceTag,
  641.                             UErrorCode&              err) const
  642. {
  643.   if(U_FAILURE(err)) 
  644.     return NULL;
  645.   
  646.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  647.   if(data != 0 
  648.      && data->getDynamicClassID() == StringList::getStaticClassID() 
  649.      && ((StringList*)data)->fCount == 1) {
  650.     return &(((StringList*)data)->fStrings[0]);
  651.   }
  652.   else err = U_MISSING_RESOURCE_ERROR;
  653.   return NULL;
  654. }
  655.  
  656. const UnicodeString*
  657. ResourceBundle::getStringArray( const UnicodeString&    resourceTag,
  658.                                 int32_t&                count,
  659.                                 UErrorCode&              err) const
  660. {
  661.   if(U_FAILURE(err)) 
  662.     return 0;
  663.   
  664.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  665.   if(data != 0 
  666.      && data->getDynamicClassID() == StringList::getStaticClassID()) {
  667.     count = ((StringList*)data)->fCount;
  668.     return ((StringList*)data)->fStrings;
  669.   }
  670.   err = U_MISSING_RESOURCE_ERROR;
  671.   return 0;
  672. }
  673.  
  674. void
  675. ResourceBundle::getArrayItem(   const UnicodeString&    resourceTag,
  676.                                 int32_t                 index,
  677.                                 UnicodeString&          theArrayItem,
  678.                                 UErrorCode&              err) const
  679. {
  680.   if(U_FAILURE(err)) 
  681.     return;
  682.  
  683.   const UnicodeString* temp = getArrayItem(resourceTag, index, err);
  684.   if(U_SUCCESS(err))
  685.     theArrayItem = *temp;
  686. }
  687.  
  688. const UnicodeString*
  689. ResourceBundle::getArrayItem(   const UnicodeString&    resourceTag,
  690.                                 int32_t                 index,
  691.                                 UErrorCode&              err) const
  692. {
  693.   if(U_FAILURE(err)) 
  694.     return NULL;
  695.  
  696.   // Casting to unsigned turns a signed value into a large unsigned
  697.   // value.  This allows us to do one comparison to check that 0 <=
  698.   // index < count, instead of two separate comparisons for each index
  699.   // check.
  700.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  701.   if(data != 0 
  702.      && data->getDynamicClassID() == StringList::getStaticClassID() 
  703.      && (uint32_t)index < (uint32_t)((StringList*)data)->fCount) {
  704.     return &(((StringList*)data)->fStrings[index]);
  705.   }
  706.   else
  707.     err = U_MISSING_RESOURCE_ERROR;
  708.   return NULL;
  709. }
  710.  
  711. const UnicodeString** 
  712. ResourceBundle::get2dArray(const UnicodeString&   resourceTag,
  713.                int32_t&             rowCount,
  714.                int32_t&             columnCount,
  715.                UErrorCode&           err) const
  716. {
  717.   if(U_FAILURE(err)) 
  718.     return 0;
  719.  
  720.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  721.   if(data != 0 
  722.      && data->getDynamicClassID() == String2dList::getStaticClassID()) {
  723.     String2dList *list = (String2dList*)data;
  724.     rowCount = list->fRowCount;
  725.     columnCount = list->fColCount;
  726.     // Why is this cast required? It shouldn't be. [LIU]
  727.     return (const UnicodeString**)list->fStrings; 
  728.   }
  729.   err = U_MISSING_RESOURCE_ERROR;
  730.   return 0;
  731. }
  732.  
  733. void
  734. ResourceBundle::get2dArrayItem(const UnicodeString&    resourceTag,
  735.                    int32_t              rowIndex,
  736.                    int32_t              columnIndex,
  737.                    UnicodeString&       theArrayItem,
  738.                    UErrorCode&           err) const
  739. {
  740.   if(U_FAILURE(err)) 
  741.     return;
  742.   
  743.   const UnicodeString* temp = get2dArrayItem(resourceTag, rowIndex, 
  744.                          columnIndex, err);
  745.   
  746.   if(U_SUCCESS(err))
  747.     theArrayItem = *temp;
  748. }
  749.  
  750. const UnicodeString*
  751. ResourceBundle::get2dArrayItem(const UnicodeString&    resourceTag,
  752.                    int32_t              rowIndex,
  753.                    int32_t              columnIndex,
  754.                    UErrorCode&           err) const
  755. {
  756.   if(U_FAILURE(err)) 
  757.     return NULL;
  758.  
  759.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  760.   if(data != 0 
  761.      && data->getDynamicClassID() == String2dList::getStaticClassID()) {
  762.     String2dList *list = (String2dList*)data;
  763.     // Casting to unsigned turns a signed value into a large unsigned
  764.     // value.  This allows us to do one comparison to check that 0 <=
  765.     // index < count, instead of two separate comparisons for each
  766.     // index check.
  767.     if(((uint32_t)rowIndex) < (uint32_t)(list->fRowCount) 
  768.        && ((uint32_t)columnIndex) < (uint32_t)(list->fColCount)) {
  769.       return &(list->fStrings[rowIndex][columnIndex]);
  770.     }
  771.   }
  772.   err = U_MISSING_RESOURCE_ERROR;
  773.   return NULL;
  774. }
  775.  
  776. void
  777. ResourceBundle::getTaggedArrayItem( const UnicodeString&    resourceTag,
  778.                                     const UnicodeString&    itemTag,
  779.                                     UnicodeString&          theArrayItem,
  780.                                     UErrorCode&              err) const
  781. {
  782.   if(U_FAILURE(err)) 
  783.     return;
  784.   
  785.   const UnicodeString* temp = getTaggedArrayItem(resourceTag, itemTag, err);
  786.     
  787.   if(U_SUCCESS(err)) 
  788.     theArrayItem = *temp;
  789. }
  790.  
  791. const UnicodeString*
  792. ResourceBundle::getTaggedArrayItem( const UnicodeString&    resourceTag,
  793.                                     const UnicodeString&    itemTag,
  794.                                     UErrorCode&              err) const
  795. {
  796.   if(U_FAILURE(err)) 
  797.     return NULL;
  798.  
  799.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  800.   if(data != 0 
  801.      && data->getDynamicClassID() == TaggedList::getStaticClassID()) {
  802.     const UnicodeString* s = ((TaggedList*)data)->get(itemTag);
  803.     if(s != 0)  
  804.       return s;
  805.   }
  806.   
  807.   err = U_MISSING_RESOURCE_ERROR;
  808.   return NULL;
  809. }
  810.  
  811. extern "C" void 
  812. T_ResourceBundle_getTaggedArrayUChars(const ResourceBundle*   bundle,
  813.                       const UnicodeString&    resourceTag,
  814.                       UChar const**         itemTags,
  815.                       UChar const**         items,
  816.                       int32_t                    maxItems,
  817.                       int32_t*                numItems,
  818.                       UErrorCode*              err)
  819. {
  820.   // this function is here solely because there seems to be no way to
  821.   // declare an extern "C" function as a friend of a class.  So we
  822.   // have a function with ordinary C++ linkage that is a friend of
  823.   // ResourceBundle and does the work, and a hidden method with C
  824.   // linkage that calls it and is used by the C wrappers.  Disgusting,
  825.   // isn't it?  This was all rtg's idea.  --jf 12/16/98
  826.   getTaggedArrayUCharsImplementation(bundle, resourceTag,
  827.                      itemTags, items, maxItems, 
  828.                      *numItems, *err);
  829. }
  830.  
  831. void 
  832. getTaggedArrayUCharsImplementation( const ResourceBundle*   bundle,
  833.                     const UnicodeString&    resourceTag,
  834.                     UChar const**         itemTags,
  835.                     UChar const**     items,
  836.                     int32_t        maxItems,
  837.                     int32_t&          numItems,
  838.                     UErrorCode&              err)
  839. {
  840.   // this is here solely to support the C implementation of
  841.   // ResourceBundle.  This function isn't defined as part of the API;
  842.   // The C wrappers know it's here and define it on their own.  --jf
  843.   // 12/16/98
  844.   if(U_FAILURE(err)) 
  845.     return;
  846.  
  847.   const ResourceBundleData* data = bundle->getDataForTag(resourceTag, err);
  848.   if(U_FAILURE(err) || data == 0 
  849.      || data->getDynamicClassID() != TaggedList::getStaticClassID()) {
  850.     err = U_MISSING_RESOURCE_ERROR;
  851.     return;
  852.   }
  853.   
  854.   UHashtable* forEnumerationValues = ((TaggedList*)data)->fHashtableValues;
  855.   void*                   value;
  856.   
  857.   numItems = 0;
  858.   int32_t pos = -1;
  859.   while(value = uhash_nextElement(forEnumerationValues, &pos)) {
  860.     if(numItems < maxItems) {
  861.       itemTags[numItems] = 
  862.     ((const UnicodeString*)uhash_get(((TaggedList*)data)->fHashtableKeys,
  863.                      numItems+1))->getUChars();
  864.       items[numItems] = ((const UnicodeString*)value)->getUChars();
  865.     }
  866.     numItems++;
  867.   }
  868. }
  869.  
  870. void
  871. ResourceBundle::getTaggedArray( const UnicodeString&    resourceTag,
  872.                                 UnicodeString*&         itemTags,
  873.                                 UnicodeString*&         items,
  874.                                 int32_t&                numItems,
  875.                                 UErrorCode&              err) const
  876. {
  877.   if(U_FAILURE(err)) 
  878.     return;
  879.  
  880.   const ResourceBundleData* data = getDataForTag(resourceTag, err);
  881.   if(U_FAILURE(err) || data == 0 
  882.      || data->getDynamicClassID() != TaggedList::getStaticClassID()) {
  883.     err = U_MISSING_RESOURCE_ERROR;
  884.     return;
  885.   }
  886.   
  887.   // go through the resource once and count how many items there are
  888.   
  889.   numItems = uhash_size(((TaggedList*)data)->fHashtableValues);
  890.   
  891.   // now create the string arrays and go through the hash table again, this
  892.   // time copying the keys and values into the string arrays
  893.   itemTags = new UnicodeString[numItems];
  894.   items = new UnicodeString[numItems];
  895.   
  896.   UHashtable* forEnumerationValues = ((TaggedList*)data)->fHashtableValues;
  897.   void*                   value;
  898.     
  899.   numItems = 0;
  900.   int32_t pos = -1;
  901.   while(value = uhash_nextElement(forEnumerationValues, &pos)) {
  902.     itemTags[numItems] = 
  903.       *((const UnicodeString*)uhash_get(((TaggedList*)data)->fHashtableKeys, 
  904.                     numItems+1));
  905.     items[numItems] = *((const UnicodeString*)value);
  906.     numItems++;
  907.   }
  908. }
  909.  
  910. const char*
  911. ResourceBundle::getVersionNumber()  const
  912. {
  913.   if(fVersionID == 0) {
  914.     // If the version ID has not been built yet, then do so.  Retrieve
  915.     // the minor version from the file.
  916.     UErrorCode status = U_ZERO_ERROR;
  917.     UnicodeString minor_version;
  918.     getString(kVersionTag, minor_version, status);
  919.     
  920.     // Determine the length of of the final version string.  This is
  921.     // the length of the major part + the length of the separator
  922.     // (==1) + the length of the minor part (+ 1 for the zero byte at
  923.     // the end).
  924.     int32_t len = icu_strlen(ICU_VERSION);
  925.     int32_t minor_len = 0;
  926.     if(U_SUCCESS(status) && minor_version.size() > 0) 
  927.       minor_len = minor_version.size();
  928.     len += (minor_len > 0) ? minor_len : 1 /*==icu_strlen(kDefaultMinorVersion)*/;
  929.     ++len; // Add length of separator
  930.     
  931.     // Allocate the string, and build it up.
  932.     // + 1 for zero byte
  933.     ((ResourceBundle*)this)->fVersionID = new char[1 + len]; 
  934.     
  935.     icu_strcpy(fVersionID, ICU_VERSION);
  936.     icu_strcat(fVersionID, kVersionSeparator);
  937.     if(minor_len > 0) {
  938.       minor_version.extract(0, minor_len, fVersionID + len - minor_len);
  939.       fVersionID[len] =  0;
  940.     }
  941.     else {
  942.       icu_strcat(fVersionID, kDefaultMinorVersion);
  943.     }
  944.   }
  945.   return fVersionID;
  946. }
  947.  
  948. const UnicodeString*
  949. ResourceBundle::listInstalledLocales(const UnicodeString& path,
  950.                                      int32_t&   numInstalledLocales)
  951. {
  952.   const UHashtable* h = getFromCache(PathInfo(path, kDefaultSuffix), 
  953.                      kIndexLocaleName, fgUserCache);
  954.   
  955.   if(h == 0) {
  956.     UErrorCode error = U_ZERO_ERROR;
  957.     if(parseIfUnparsed(PathInfo(path, kDefaultSuffix), 
  958.                kIndexFilename, fgUserCache, 
  959.                fgUserVisitedFiles, error)) {
  960.       h = getFromCache(PathInfo(path, kDefaultSuffix), 
  961.                kIndexLocaleName, fgUserCache);
  962.     }
  963.   }
  964.   
  965.   if(h != 0) {
  966.     UnicodeString ukIndexTag = kIndexTag;
  967.     ResourceBundleData *data = 
  968.       (ResourceBundleData*) uhash_get(h, ukIndexTag.hashCode() & 0x7FFFFFFF);
  969.     if(data != 0 
  970.        && data->getDynamicClassID() == StringList::getStaticClassID()) {
  971.       numInstalledLocales = ((StringList*)data)->fCount;
  972.       return ((StringList*)data)->fStrings;
  973.     }
  974.   }
  975.   
  976.   numInstalledLocales = 0;
  977.   return 0;
  978. }
  979.  
  980. extern "C" const UnicodeString** 
  981. T_ResourceBundle_listInstalledLocales(const char* path,
  982.                       int32_t* numInstalledLocales) 
  983. {
  984.   // this is here solely to support the C implementation of Locale.
  985.   // This function isn't defined as part of the API; T_Locale knows
  986.   // it's here and defines it on its own.  --rtg 11/28/98
  987.   
  988.   return listInstalledLocalesImplementation(path, numInstalledLocales);
  989. }
  990.  
  991. const UnicodeString** 
  992. listInstalledLocalesImplementation(const char* path,
  993.                    int32_t* numInstalledLocales) 
  994. {
  995.   // this function is here solely because there seems to be no way to
  996.   // declare an extern "C" function as a friend of a class.  So we
  997.   // have a function with ordinary C++ linkage that is a friend of
  998.   // ResourceBundle and does the work, and a hidden method with C
  999.   // linkage that calls it and is used by the C implementation of
  1000.   // Locale.  Disgusting, isn't it?  --rtg 11/30/98
  1001.   const UnicodeString* array = (ResourceBundle::listInstalledLocales(path, *numInstalledLocales));
  1002.   const UnicodeString**  arrayOfPtrs = (const UnicodeString**) new UnicodeString*[*numInstalledLocales];
  1003.   for(int i = 0; i < *numInstalledLocales; i++)
  1004.     arrayOfPtrs[i] = &array[i];
  1005.   return arrayOfPtrs;
  1006. }
  1007.  
  1008. int32_t
  1009. T_ResourceBundle_countArrayItemsImplementation(const ResourceBundle* resourceBundle, 
  1010.                            const char* resourceKey,
  1011.                            UErrorCode& err) 
  1012. {
  1013.   if(U_FAILURE(err)) 
  1014.     return 0;
  1015.  
  1016.   if(!resourceKey) {
  1017.     err = U_ILLEGAL_ARGUMENT_ERROR;
  1018.     return 0;
  1019.   }
  1020.   const ResourceBundleData* data  = resourceBundle->getDataForTag(resourceKey,
  1021.                                   err);
  1022.   if(U_FAILURE(err)) 
  1023.     return 0;
  1024.   
  1025.   UClassID rbkeyClassID = data->getDynamicClassID();
  1026.   int32_t numItems = 0;
  1027.   
  1028.   if(rbkeyClassID == StringList::getStaticClassID()) {
  1029.     numItems = ((StringList*)data)->fCount;
  1030.   }
  1031.   else if(rbkeyClassID == TaggedList::getStaticClassID()) {
  1032.     numItems =  uhash_size(((TaggedList*)data)->fHashtableValues);
  1033.   }
  1034.   else if(rbkeyClassID == String2dList::getStaticClassID()) {
  1035.     numItems = ((String2dList*)data)->fRowCount; 
  1036.   }
  1037.   else {
  1038.     err = U_MISSING_RESOURCE_ERROR;
  1039.     return 0;
  1040.   }
  1041.   
  1042.   return numItems;
  1043. }
  1044.  
  1045.  
  1046. extern "C" int32_t 
  1047. T_ResourceBundle_countArrayItems(const ResourceBundle* resourceBundle, 
  1048.                  const char* resourceKey,
  1049.                  UErrorCode* err) 
  1050. {
  1051.   return T_ResourceBundle_countArrayItemsImplementation(resourceBundle,
  1052.                             resourceKey,
  1053.                             *err);
  1054. }
  1055.  
  1056. /**
  1057.  * Retrieve a ResourceBundle from the cache.  Return NULL if not found.
  1058.  */
  1059. const UHashtable* 
  1060. ResourceBundle::getFromCache(const PathInfo& path,
  1061.                  const UnicodeString& localeName,
  1062.                  ResourceBundleCache* fgCache)
  1063. {
  1064.     UnicodeString keyname(path.makeHashkey(localeName));
  1065.     Mutex lock;
  1066.     
  1067.     return (const UHashtable*)
  1068.       uhash_get(fgCache->hashTable, keyname.hashCode() & 0x7FFFFFFF);
  1069. }
  1070.  
  1071. /**
  1072.  * Parse a file, storing the resource data in the cache.
  1073.  */
  1074. void 
  1075. ResourceBundle::parse(const PathInfo& path,
  1076.               const UnicodeString& locale,
  1077.               Handler handler,
  1078.               void *context,
  1079.               ResourceBundleCache *fgCache,
  1080.               UErrorCode& status)
  1081. {
  1082.   FileStream *f;
  1083.   UnicodeString localeName;
  1084.   UHashtable *data;
  1085.   
  1086.   if (U_FAILURE(status)) return;
  1087.   
  1088.   f = path.openFile(locale);
  1089.   if(f == 0) {
  1090.     status = U_FILE_ACCESS_ERROR;
  1091.     return;
  1092.   }
  1093.   
  1094.   /* Get the data from the compiled resource bundle file */
  1095.   data = rb_parse(f, localeName, status);
  1096.  
  1097.   /* Close the file */
  1098.   T_FileStream_close(f);
  1099.  
  1100.   if(U_FAILURE(status)) {
  1101.     return;
  1102.   }
  1103.  
  1104.   /* Invoke the handler function */
  1105.   handler(localeName, data, context, fgCache);
  1106. }
  1107.  
  1108. void
  1109. ResourceBundle::addToCache(const UnicodeString& localeName,
  1110.                UHashtable* hashtable,
  1111.                void* context,
  1112.                ResourceBundleCache* fgCache)
  1113. {
  1114.   PathInfo *c = (PathInfo*)context;
  1115.   UnicodeString keyName(c->makeHashkey(localeName));
  1116.   UErrorCode err = U_ZERO_ERROR;
  1117.   Mutex lock;
  1118.   if(uhash_get(fgCache->hashTable, keyName.hashCode() & 0x7FFFFFFF) == 0) {
  1119.     uhash_putKey(fgCache->hashTable, keyName.hashCode() & 0x7FFFFFFF, 
  1120.          hashtable, &err);
  1121.   }
  1122. }
  1123.  
  1124. ResourceBundle::PathInfo::PathInfo()
  1125.   : fWPrefix(NULL), fWSuffix(NULL)
  1126. {}
  1127.  
  1128. ResourceBundle::PathInfo::PathInfo(const PathInfo& source) 
  1129.   : fPrefix(source.fPrefix), 
  1130.     fSuffix(source.fSuffix), 
  1131.     fWPrefix(NULL), fWSuffix(NULL)
  1132. {
  1133.   if(source.fWPrefix) {
  1134.     fWPrefix = new wchar_t[icu_wcslen(source.fWPrefix)+1];
  1135.     fWSuffix = new wchar_t[icu_wcslen(source.fWSuffix)+1];
  1136.     icu_wcscpy(fWPrefix, source.fWPrefix);
  1137.     icu_wcscpy(fWSuffix, source.fWSuffix);
  1138.   }
  1139. }
  1140.  
  1141. ResourceBundle::PathInfo::PathInfo(const UnicodeString& path)
  1142.   : fPrefix(path), 
  1143.     fWPrefix(NULL), 
  1144.     fWSuffix(NULL)
  1145. {}
  1146.  
  1147. ResourceBundle::PathInfo::PathInfo(const UnicodeString& path, 
  1148.                    const UnicodeString& suffix)
  1149.   : fPrefix(path), 
  1150.     fSuffix(suffix), 
  1151.     fWPrefix(NULL), 
  1152.     fWSuffix(NULL)
  1153. {}
  1154.  
  1155. ResourceBundle::PathInfo::PathInfo(const wchar_t* path,
  1156.                    const wchar_t* suffix)
  1157.   : fPrefix(), 
  1158.     fSuffix(), 
  1159.     fWPrefix(NULL), 
  1160.     fWSuffix(NULL)
  1161. {
  1162.   fWPrefix = new wchar_t[icu_wcslen(path)+1];
  1163.   fWSuffix = new wchar_t[icu_wcslen(suffix)+1];
  1164.   icu_wcscpy(fWPrefix, path);
  1165.   icu_wcscpy(fWSuffix, suffix);
  1166. }
  1167.  
  1168. ResourceBundle::PathInfo::~PathInfo()
  1169. {
  1170.   delete [] fWPrefix;
  1171.   delete [] fWSuffix;
  1172. }
  1173.  
  1174. ResourceBundle::PathInfo&
  1175. ResourceBundle::PathInfo::operator=(const PathInfo& source)
  1176. {
  1177.   if(this != &source) {
  1178.     wchar_t* tempPref = NULL;
  1179.     wchar_t* tempSuff = NULL;
  1180.     if(source.fWPrefix) {
  1181.       tempPref = new wchar_t[icu_wcslen(source.fWPrefix)+1];
  1182.       tempSuff = new wchar_t[icu_wcslen(source.fWSuffix)+1];
  1183.       icu_wcscpy(tempPref, source.fWPrefix);
  1184.       icu_wcscpy(tempSuff, source.fWSuffix);
  1185.     }
  1186.     delete fWPrefix;
  1187.     fWPrefix = tempPref;
  1188.     delete fWSuffix;
  1189.     fWSuffix = tempSuff;
  1190.     fPrefix = source.fPrefix;
  1191.     fSuffix = source.fSuffix;
  1192.   }
  1193.   return *this;
  1194. }
  1195.  
  1196. bool_t 
  1197. ResourceBundle::PathInfo::fileExists(const UnicodeString& localeName) const
  1198. {
  1199.   FileStream *temp = openFile(localeName);
  1200.   if(temp) {
  1201.     T_FileStream_close(temp);
  1202.     return TRUE;
  1203.   }
  1204.   else {
  1205.     return FALSE;
  1206.   }
  1207. }
  1208.  
  1209. UnicodeString 
  1210. ResourceBundle::PathInfo::makeCacheKey(const UnicodeString& name) const
  1211. {
  1212.   if(fWPrefix) {
  1213.     UnicodeString key;
  1214.     
  1215.     size_t prefSize = icu_wcstombs(NULL, fWPrefix, ((size_t)-1) >> 1);
  1216.     size_t suffSize = icu_wcstombs(NULL, fWSuffix, ((size_t)-1) >> 1);
  1217.     size_t tempSize = icu_max((int32_t)prefSize, (int32_t)suffSize);
  1218.     char *temp = new char[tempSize + 1];
  1219.  
  1220.     tempSize = icu_wcstombs(temp, fWPrefix, prefSize);
  1221.     temp[tempSize] = 0;
  1222.     key += UnicodeString(temp);
  1223.     
  1224.     key += name;
  1225.     
  1226.     tempSize = icu_wcstombs(temp, fWSuffix, suffSize);
  1227.     temp[tempSize] = 0;
  1228.     key += UnicodeString(temp);
  1229.     
  1230.     delete [] temp;
  1231.     
  1232.     return key;
  1233.   } 
  1234.   else {
  1235.     UnicodeString workingName(fPrefix);
  1236.     workingName += name;
  1237.     workingName += fSuffix;
  1238.     
  1239.     return workingName;
  1240.   }
  1241. }
  1242.  
  1243. UnicodeString 
  1244. ResourceBundle::PathInfo::makeHashkey(const UnicodeString& localeName) const
  1245. {
  1246.   if(fWPrefix) {
  1247.     UnicodeString key(localeName);
  1248.     
  1249.     key += kSeparator;
  1250.     
  1251.     size_t prefSize = icu_wcstombs(NULL, fWPrefix, ((size_t)-1) >> 1);
  1252.     size_t suffSize = icu_wcstombs(NULL, fWSuffix, ((size_t)-1) >> 1);
  1253.     size_t tempSize = icu_max((int32_t)prefSize, (int32_t)suffSize);
  1254.     char *temp = new char[tempSize + 1];
  1255.     
  1256.     tempSize = icu_wcstombs(temp, fWSuffix, suffSize);
  1257.     temp[tempSize] = 0;
  1258.     key += UnicodeString(temp);
  1259.     
  1260.     key += kSeparator;
  1261.     
  1262.     tempSize = icu_wcstombs(temp, fWPrefix, prefSize);
  1263.     temp[tempSize] = 0;
  1264.     key += UnicodeString(temp);
  1265.     
  1266.     delete [] temp;
  1267.     
  1268.     return key;
  1269.   }
  1270.   else {
  1271.     UnicodeString keyName = localeName;
  1272.     keyName += kSeparator;
  1273.     keyName += fSuffix;
  1274.     keyName += kSeparator;
  1275.     keyName += fPrefix;
  1276.     return keyName;
  1277.   }
  1278. }
  1279.  
  1280. FileStream* 
  1281. ResourceBundle::PathInfo::openFile(const UnicodeString& localeName) const
  1282. {
  1283.   if(fWPrefix) {
  1284.     //use the wide version of fopen in TPlatformUtilities.
  1285.     int32_t nameSize = localeName.size();
  1286.     char* temp = new char[nameSize + 1];
  1287.     localeName.extract(0, nameSize, temp);
  1288.     temp[nameSize] = 0;
  1289.     int32_t wideNameLen = icu_mbstowcs(NULL, temp, nameSize);
  1290.     wchar_t* wideName = new wchar_t[wideNameLen + 1];
  1291.     icu_mbstowcs(wideName, temp, nameSize);
  1292.     wideName[wideNameLen] = 0;
  1293.     delete [] temp;
  1294.     
  1295.     size_t prefLen = icu_wcslen(fWPrefix);
  1296.     size_t suffLen = icu_wcslen(fWSuffix);
  1297.     
  1298.     int32_t destSize = prefLen + suffLen + wideNameLen;
  1299.     wchar_t* dest = new wchar_t[destSize + 1];
  1300.     icu_wcscpy(dest, fWPrefix);
  1301.     dest[prefLen] = 0;
  1302.     
  1303.     icu_wcscat(dest, wideName);
  1304.     dest[prefLen + wideNameLen] = 0;
  1305.     
  1306.     icu_wcscat(dest, fWSuffix);
  1307.     dest[destSize] = 0;
  1308.     
  1309.     int32_t fmodeLen = icu_mbstowcs(NULL, "rb", 2);
  1310.     wchar_t* fmode = new wchar_t[fmodeLen + 1];
  1311.     icu_mbstowcs(fmode, "rb", 2);
  1312.     fmode[fmodeLen] = 0;
  1313.  
  1314.     FileStream* result = T_FileStream_wopen(dest, fmode);
  1315.     
  1316.     delete [] fmode;
  1317.     delete [] dest;
  1318.     delete [] wideName;
  1319.     return result;
  1320.   } 
  1321.   else {
  1322.     //open file using standard char* routines
  1323.     UnicodeString workingName(makeCacheKey(localeName));
  1324.     int32_t size = workingName.size();
  1325.     char* returnVal = new char[size + 1];
  1326.     workingName.extract(0, size, returnVal);
  1327.     returnVal[size] = 0;
  1328.     FileStream* result = T_FileStream_open(returnVal, "rb");
  1329.     delete [] returnVal;
  1330.     return result;
  1331.   }
  1332. }
  1333.  
  1334. const UChar ResourceBundle::PathInfo::kSeparator = 0xF8FF;
  1335.  
  1336. //eof
  1337.