2d26f13d40
X-SVN-Rev: 343
1344 lines
43 KiB
C++
1344 lines
43 KiB
C++
/*
|
|
*******************************************************************************
|
|
* *
|
|
* COPYRIGHT: *
|
|
* (C) Copyright Taligent, Inc., 1997 *
|
|
* (C) Copyright International Business Machines Corporation, 1997-1999 *
|
|
* Licensed Material - Program-Property of IBM - All Rights Reserved. *
|
|
* US Government Users Restricted Rights - Use, duplication, or disclosure *
|
|
* restricted by GSA ADP Schedule Contract with IBM Corp. *
|
|
* *
|
|
*******************************************************************************
|
|
*
|
|
* File resbund.cpp
|
|
*
|
|
* Modification History:
|
|
*
|
|
* Date Name Description
|
|
* 02/05/97 aliu Fixed bug in chopLocale. Added scanForLocaleInFile
|
|
* based on code taken from scanForLocale. Added
|
|
* constructor which attempts to read resource bundle
|
|
* from a specific file, without searching other files.
|
|
* 02/11/97 aliu Added UErrorCode return values to constructors. Fixed
|
|
* infinite loops in scanForFile and scanForLocale.
|
|
* Modified getRawResourceData to not delete storage in
|
|
* localeData and resourceData which it doesn't own.
|
|
* Added Mac compatibility #ifdefs for tellp() and
|
|
* ios::nocreate.
|
|
* 03/04/97 aliu Modified to use ExpandingDataSink objects instead of
|
|
* the highly inefficient ostrstream objects.
|
|
* 03/13/97 aliu Rewrote to load in entire resource bundle and store
|
|
* it as a Hashtable of ResourceBundleData objects.
|
|
* Added state table to govern parsing of files.
|
|
* Modified to load locale index out of new file distinct
|
|
* from default.txt.
|
|
* 03/25/97 aliu Modified to support 2-d arrays, needed for timezone data.
|
|
* Added support for custom file suffixes. Again, needed to
|
|
* support timezone data. Improved error handling to detect
|
|
* duplicate tags and subtags.
|
|
* 04/07/97 aliu Fixed bug in getHashtableForLocale(). Fixed handling of
|
|
* failing UErrorCode values on entry to API methods.
|
|
* Fixed bugs in getArrayItem() for negative indices.
|
|
* 04/29/97 aliu Update to use new Hashtable deletion protocol.
|
|
* 05/06/97 aliu Flattened kTransitionTable for HP compiler. Fixed usage of
|
|
* CharString.
|
|
* 06/11/99 stephen Removed parsing of .txt files.
|
|
* Reworked to use new binary format.
|
|
* Cleaned up.
|
|
* 06/14/99 stephen Removed methods taking a filename suffix.
|
|
* 06/22/99 stephen Added missing T_FileStream_close in parse()
|
|
* 11/09/99 weiv Added getLocale(), rewritten constructForLocale()
|
|
*******************************************************************************
|
|
*/
|
|
|
|
#include "rbcache.h"
|
|
|
|
#include "resbund.h"
|
|
#include "mutex.h"
|
|
|
|
#include "unistrm.h"
|
|
#include "filestrm.h"
|
|
#include "cstring.h"
|
|
#include "uhash.h"
|
|
|
|
#include "rbdata.h"
|
|
#include "rbread.h"
|
|
|
|
#include <iostream.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* Implementation Notes
|
|
*
|
|
* Resource bundles are read in once, and thereafter cached.
|
|
* ResourceBundle statically keeps track of which files have been
|
|
* read, so we are guaranteed that each file is read at most once.
|
|
* Resource bundles can be loaded from different data directories and
|
|
* will be treated as distinct, even if they are for the same locale.
|
|
*
|
|
* Resource bundles are lightweight objects, which have pointers to
|
|
* one or more shared Hashtable objects containing all the data.
|
|
* Copying would be cheap, but there is no copy constructor, since
|
|
* there wasn't one in the original API.
|
|
*
|
|
* The ResourceBundle parsing mechanism is implemented as a transition
|
|
* network, for easy maintenance and modification. The network is
|
|
* implemented as a matrix (instead of in code) to make this even
|
|
* easier. The matrix contains Transition objects. Each Transition
|
|
* object describes a destination node and an action to take before
|
|
* moving to the destination node. The source node is encoded by the
|
|
* index of the object in the array that contains it. The pieces
|
|
* needed to understand the transition network are the enums for node
|
|
* IDs and actions, the parse() method, which walks through the
|
|
* network and implements the actions, and the network itself. The
|
|
* network guarantees certain conditions, for example, that a new
|
|
* resource will not be closed until one has been opened first; or
|
|
* that data will not be stored into a TaggedList until a TaggedList
|
|
* has been created. Nonetheless, the code in parse() does some
|
|
* consistency checks as it runs the network, and fails with an
|
|
* U_INTERNAL_PROGRAM_ERROR if one of these checks fails. If the input
|
|
* data has a bad format, an U_INVALID_FORMAT_ERROR is returned. If you
|
|
* see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in
|
|
* it.
|
|
*
|
|
* Old functionality of multiple locales in a single file is still
|
|
* supported. For this reason, LOCALE names override FILE names. If
|
|
* data for en_US is located in the en.txt file, once it is loaded,
|
|
* the code will not care where it came from (other than remembering
|
|
* which directory it came from). However, if there is an en_US
|
|
* resource in en_US.txt, that will take precedence. There is no
|
|
* limit to the number or type of resources that can be stored in a
|
|
* file, however, files are only searched in a specific way. If
|
|
* en_US_CA is requested, then first en_US_CA.txt is searched, then
|
|
* en_US.txt, then en.txt, then default.txt. So it only makes sense
|
|
* to put certain locales in certain files. In this example, it would
|
|
* be logical to put en_US_CA, en_US, and en into the en.txt file,
|
|
* since they would be found there if asked for. The extreme example
|
|
* is to place all locale resources into default.txt, which should
|
|
* also work.
|
|
*
|
|
* Inheritance is implemented. For example, xx_YY_zz inherits as
|
|
* follows: xx_YY_zz, xx_YY, xx, default. Inheritance is implemented
|
|
* as an array of hashtables. There will be from 1 to 4 hashtables in
|
|
* the array.
|
|
*
|
|
* Fallback files are implemented. The fallback pattern is Language
|
|
* Country Variant (LCV) -> LC -> L. Fallback is first done for the
|
|
* requested locale. Then it is done for the default locale, as
|
|
* returned by Locale::getDefault(). Then the special file
|
|
* default.txt is searched for the default locale. The overall FILE
|
|
* fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default.
|
|
*
|
|
* Note that although file name searching includes the default locale,
|
|
* once a ResourceBundle object is constructed, the inheritance path
|
|
* no longer includes the default locale. The path is LCV -> LC -> L
|
|
* -> default.
|
|
*
|
|
* File parsing is lazy. Nothing is parsed unless it is called for by
|
|
* someone. So when a ResourceBundle for xx_YY_zz is constructed,
|
|
* only that locale is parsed (along with anything else in the same
|
|
* file). Later, if the FooBar tag is asked for, and if it isn't
|
|
* found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and
|
|
* so forth, until the chain is exhausted or the tag is found.
|
|
*
|
|
* Thread-safety is implemented around caches, both the cache that
|
|
* stores all the resouce data, and the cache that stores flags
|
|
* indicating whether or not a file has been visited. These caches
|
|
* delete their storage at static cleanup time, when the process
|
|
* quits.
|
|
*
|
|
* ResourceBundle supports TableCollation as a special case. This
|
|
* involves having special ResourceBundle objects which DO own their
|
|
* data, since we don't want large collation rule strings in the
|
|
* ResourceBundle cache (these are already cached in the
|
|
* TableCollation cache). TableCollation files (.ctx files) have the
|
|
* same format as normal resource data files, with a different
|
|
* interpretation, from the standpoint of ResourceBundle. .ctx files
|
|
* are loaded into otherwise ordinary ResourceBundle objects. They
|
|
* don't inherit (that's implemented by TableCollation) and they own
|
|
* their data (as mentioned above). However, they still support
|
|
* possible multiple locales in a single .ctx file. (This is in
|
|
* practice a bad idea, since you only want the one locale you're
|
|
* looking for, and only one tag will be present
|
|
* ("CollationElements"), so you don't need an inheritance chain of
|
|
* multiple locales.) Up to 4 locale resources will be loaded from a
|
|
* .ctx file; everything after the first 4 is ignored (parsed and
|
|
* deleted). (Normal .txt files have no limit.) Instead of being
|
|
* loaded into the cache, and then looked up as needed, the locale
|
|
* resources are read straight into the ResourceBundle object.
|
|
*
|
|
* The Index, which used to reside in default.txt, has been moved to a
|
|
* new file, index.txt. This file contains a slightly modified format
|
|
* with the addition of the "InstalledLocales" tag; it looks like:
|
|
*
|
|
* Index {
|
|
* InstalledLocales {
|
|
* ar
|
|
* ..
|
|
* zh_TW
|
|
* }
|
|
* }
|
|
*/
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const char* ResourceBundle::kDefaultSuffix = ".res";
|
|
const int32_t ResourceBundle::kDefaultSuffixLen = 4;
|
|
const char* ResourceBundle::kDefaultFilename = "default";
|
|
const char* ResourceBundle::kDefaultLocaleName = "default";
|
|
const char* ResourceBundle::kIndexLocaleName = "index";
|
|
const char* ResourceBundle::kIndexFilename = "index";
|
|
const char* ResourceBundle::kIndexTag = "InstalledLocales";
|
|
|
|
// The default minor version and the version separator must be exactly one
|
|
// character long.
|
|
const char* ResourceBundle::kDefaultMinorVersion = "0";
|
|
const char* ResourceBundle::kVersionSeparator = ".";
|
|
const char* ResourceBundle::kVersionTag = "Version";
|
|
|
|
ResourceBundleCache* ResourceBundle::fgUserCache = new ResourceBundleCache();
|
|
VisitedFileCache* ResourceBundle::fgUserVisitedFiles = new VisitedFileCache();
|
|
// allocated on the heap so we don't have to expose the definitions of
|
|
// these classes to the world
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ResourceBundle::LocaleFallbackIterator::LocaleFallbackIterator(const UnicodeString& startingLocale,
|
|
const UnicodeString& root,
|
|
bool_t useDefaultLocale)
|
|
: fLocale(startingLocale),
|
|
fRoot(root),
|
|
fUseDefaultLocale(useDefaultLocale),
|
|
fTriedDefaultLocale(FALSE),
|
|
fTriedRoot(FALSE)
|
|
{
|
|
if (fUseDefaultLocale) Locale::getDefault().getName(fDefaultLocale);
|
|
}
|
|
|
|
bool_t
|
|
ResourceBundle::LocaleFallbackIterator::nextLocale(UErrorCode& status)
|
|
{
|
|
if(fUseDefaultLocale)
|
|
fTriedDefaultLocale = fTriedDefaultLocale || (fLocale == fDefaultLocale);
|
|
|
|
chopLocale();
|
|
if(status != U_USING_DEFAULT_ERROR)
|
|
status = U_USING_FALLBACK_ERROR;
|
|
|
|
if(fLocale.length() == 0) {
|
|
if(fUseDefaultLocale && !fTriedDefaultLocale) {
|
|
fLocale = fDefaultLocale;
|
|
fTriedDefaultLocale = TRUE;
|
|
status = U_USING_DEFAULT_ERROR;
|
|
}
|
|
else if( ! fTriedRoot) {
|
|
fLocale = fRoot;
|
|
fTriedRoot = TRUE;
|
|
status = U_USING_DEFAULT_ERROR;
|
|
}
|
|
else {
|
|
status = U_MISSING_RESOURCE_ERROR;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// cerr << "* " << fLocale << " " << u_errorName(status) << endl;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::LocaleFallbackIterator::chopLocale()
|
|
{
|
|
int32_t size = fLocale.length();
|
|
int32_t i;
|
|
|
|
for(i = size - 1; i > 0; i--)
|
|
if(fLocale[i] == 0x005F/*'_'*/)
|
|
break;
|
|
|
|
if(i < 0)
|
|
i = 0;
|
|
|
|
fLocale.remove(i, size - i);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
ResourceBundle::ResourceBundle( const UnicodeString& path,
|
|
const Locale& locale,
|
|
UErrorCode& error)
|
|
: fgCache(fgUserCache),
|
|
fgVisitedFiles(fgUserVisitedFiles)
|
|
{
|
|
constructForLocale(PathInfo(path, kDefaultSuffix), locale, error);
|
|
}
|
|
|
|
ResourceBundle::ResourceBundle( const UnicodeString& path,
|
|
UErrorCode& error)
|
|
: fgCache(fgUserCache),
|
|
fgVisitedFiles(fgUserVisitedFiles)
|
|
{
|
|
constructForLocale(PathInfo(path, kDefaultSuffix),
|
|
Locale::getDefault(), error);
|
|
}
|
|
|
|
/**
|
|
* This constructor is used by TableCollation to load a resource
|
|
* bundle from a specific file, without trying other files. This is
|
|
* used by the TableCollation caching mechanism. This is not a public
|
|
* API constructor.
|
|
*/
|
|
ResourceBundle::ResourceBundle( const UnicodeString& path,
|
|
const UnicodeString& localeName,
|
|
UErrorCode& status)
|
|
: fPath(path, UnicodeString(kDefaultSuffix,"")),
|
|
fRealLocale(localeName),
|
|
fIsDataOwned(TRUE),
|
|
fVersionID(0),
|
|
fgCache(fgUserCache),
|
|
fgVisitedFiles(fgUserVisitedFiles)
|
|
{
|
|
status = U_ZERO_ERROR;
|
|
|
|
int32_t i;
|
|
for(i = 0; i < kDataCount; ++i) {
|
|
fData[i] = 0;
|
|
fLoaded[i] = FALSE;
|
|
fDataStatus[i] = U_INTERNAL_PROGRAM_ERROR;
|
|
}
|
|
|
|
fLocaleIterator = 0;
|
|
|
|
// If the file doesn't exist, return an error
|
|
if(fPath.fileExists(localeName)) {
|
|
parse(fPath, localeName, saveCollationHashtable,
|
|
(void*)this, fgCache, status);
|
|
}
|
|
else {
|
|
status = U_MISSING_RESOURCE_ERROR;
|
|
}
|
|
|
|
// Prevent further attempts to load hashtables
|
|
for(i = 0; i < kDataCount; ++i)
|
|
fLoaded[i] = TRUE;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::saveCollationHashtable(const UnicodeString& localeName,
|
|
UHashtable* hashtable,
|
|
void* context,
|
|
ResourceBundleCache* fgCache)
|
|
{
|
|
ResourceBundle* bundle = (ResourceBundle*)context;
|
|
for(int32_t i = 0; i < kDataCount; ++i) {
|
|
if( ! bundle->fLoaded[i]) {
|
|
bundle->fData[i] = hashtable;
|
|
bundle->fLoaded[i] = TRUE;
|
|
bundle->fDataStatus[i] = U_ZERO_ERROR; /* ??? */
|
|
return;
|
|
}
|
|
}
|
|
// Out of room; discard extra data. We only expect to see one anyway.
|
|
uhash_close(hashtable);
|
|
}
|
|
|
|
ResourceBundle::ResourceBundle(const wchar_t* path,
|
|
const Locale& locale,
|
|
UErrorCode& err)
|
|
: fgCache(fgUserCache),
|
|
fgVisitedFiles(fgUserVisitedFiles)
|
|
{
|
|
int32_t wideNameLen = icu_mbstowcs(NULL, kDefaultSuffix, kDefaultSuffixLen);
|
|
wchar_t* wideName = new wchar_t[wideNameLen + 1];
|
|
icu_mbstowcs(wideName, kDefaultSuffix, kDefaultSuffixLen);
|
|
wideName[wideNameLen] = 0;
|
|
constructForLocale(PathInfo(path, wideName), locale, err);
|
|
delete [] wideName;
|
|
}
|
|
|
|
ResourceBundle::~ResourceBundle()
|
|
{
|
|
delete fLocaleIterator;
|
|
delete [] fVersionID;
|
|
|
|
if(fIsDataOwned)
|
|
for(int32_t i = 0; i < kDataCount; ++i) {
|
|
if(fData[i])
|
|
uhash_close((UHashtable*)fData[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
ResourceBundle::constructForLocale(const PathInfo& path,
|
|
const Locale& locale,
|
|
UErrorCode& error)
|
|
{
|
|
int32_t i;
|
|
fPath = path;
|
|
fIsDataOwned = FALSE;
|
|
fVersionID = 0;
|
|
|
|
// fRealLocale can be inited in three ways, see 1), 2), 3)
|
|
UnicodeString returnedLocale;
|
|
locale.getName(returnedLocale);
|
|
if (returnedLocale.length()!=0) {
|
|
// 1) Desired Locale has a name
|
|
fRealLocale = Locale(returnedLocale);
|
|
} else {
|
|
// 2) Desired Locale name is empty, so we use default locale for the system
|
|
fRealLocale = Locale(kDefaultLocaleName);
|
|
}
|
|
error = U_ZERO_ERROR;
|
|
for(i = 1; i < kDataCount; ++i) {
|
|
fData[i] = 0;
|
|
fDataStatus[i] = U_INTERNAL_PROGRAM_ERROR;
|
|
fLoaded[i] = FALSE;
|
|
}
|
|
|
|
error = U_ZERO_ERROR;
|
|
fData[0] = getHashtableForLocale(fRealLocale.getName(), returnedLocale, error);
|
|
fLoaded[0] = TRUE;
|
|
fDataStatus[0] = U_ZERO_ERROR;
|
|
if(U_SUCCESS(error))
|
|
// 3) We're unable to get the desired Locale, so we're using what is provided (fallback occured)
|
|
fRealLocale = Locale(returnedLocale);
|
|
|
|
fLocaleIterator = new LocaleFallbackIterator(fRealLocale.getName(),
|
|
kDefaultLocaleName, FALSE);
|
|
}
|
|
|
|
/**
|
|
* Return the hash table with data for the given locale. This method employs
|
|
* fallback both in files and in locale names. It returns the locale name
|
|
* which is actually used to return the data, if any.
|
|
*
|
|
* Parse all files found at the given path for the given path, in an effort
|
|
* to find data for the given locale. Use fallbacks and defaults as needed.
|
|
* Store read in file data in the cache for future use. Return the hashtable
|
|
* for the given locale, if found, or 0 if not.
|
|
*/
|
|
const UHashtable*
|
|
ResourceBundle::getHashtableForLocale(const UnicodeString& desiredLocale,
|
|
UnicodeString& returnedLocale,
|
|
UErrorCode& error)
|
|
{
|
|
if(U_FAILURE(error)) return 0;
|
|
|
|
error = U_ZERO_ERROR;
|
|
const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
|
|
if(h != 0) {
|
|
returnedLocale = desiredLocale;
|
|
return h;
|
|
}
|
|
|
|
LocaleFallbackIterator iterator(desiredLocale, kDefaultFilename, TRUE);
|
|
bool_t didTryCacheWithFallback = FALSE;
|
|
|
|
// A note on fileError. We are tracking two different error states
|
|
// here. One is that returned while iterating over different files.
|
|
// For instance, when going from de_CH.txt to de.txt we will get a
|
|
// U_USING_FALLBACK_ERROR, but we don't care -- because if de.txt
|
|
// contains the de_CH locale, it isn't a fallback, from our
|
|
// perspective. Therefore we keep file associated errors in
|
|
// fileError, apart from the error parameter.
|
|
UErrorCode fileError = U_ZERO_ERROR;
|
|
|
|
for(;;) {
|
|
// Build a filename for the locale.
|
|
if(parseIfUnparsed(fPath, iterator.getLocale(),
|
|
fgCache, fgVisitedFiles, error)) {
|
|
if(U_FAILURE(error))
|
|
return 0;
|
|
|
|
error = U_ZERO_ERROR;
|
|
h = getFromCacheWithFallback(fPath, desiredLocale,
|
|
returnedLocale, fgCache, error);
|
|
didTryCacheWithFallback = TRUE;
|
|
if(h != 0 && U_SUCCESS(error))
|
|
return h;
|
|
}
|
|
|
|
if(!iterator.nextLocale(fileError)) {
|
|
error = U_MISSING_RESOURCE_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We want to try loading from the cache will fallback at least
|
|
// once. These lines of code handle the case in which all of the
|
|
// fallback FILES have been loaded, so fgVisitedFiles keeps us from
|
|
// parsing them again. In this case we still want to make an
|
|
// attempt to load our locale from the cache.
|
|
if(didTryCacheWithFallback)
|
|
return 0;
|
|
error = U_ZERO_ERROR;
|
|
return getFromCacheWithFallback(fPath, desiredLocale,
|
|
returnedLocale, fgCache, error);
|
|
}
|
|
|
|
/**
|
|
* Return the hash table with data for the given locale. This method employs
|
|
* fallback in file names only. If data is returned, it will be exactly for
|
|
* the given locale.
|
|
*/
|
|
const UHashtable*
|
|
ResourceBundle::getHashtableForLocale(const UnicodeString& desiredLocale,
|
|
UErrorCode& error)
|
|
{
|
|
if(U_FAILURE(error))
|
|
return 0;
|
|
error = U_ZERO_ERROR;
|
|
|
|
// First try the cache
|
|
const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
|
|
if(h != 0)
|
|
return h;
|
|
|
|
// Now try files
|
|
LocaleFallbackIterator iterator(desiredLocale, kDefaultFilename, FALSE);
|
|
|
|
for(;;) {
|
|
UErrorCode parseError = U_ZERO_ERROR;
|
|
if(parseIfUnparsed(fPath, iterator.getLocale(),
|
|
fgCache, fgVisitedFiles, parseError)) {
|
|
if(U_FAILURE(parseError)) {
|
|
error = parseError;
|
|
return 0;
|
|
}
|
|
|
|
const UHashtable* h = getFromCache(fPath, desiredLocale, fgCache);
|
|
if(h != 0)
|
|
return h;
|
|
}
|
|
|
|
if(!iterator.nextLocale(error))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to retrieve a locale data hash from the cache, using fallbacks
|
|
* if necessary. Ultimately we will try to load the data under
|
|
* kDefaultLocaleName.
|
|
*/
|
|
const UHashtable*
|
|
ResourceBundle::getFromCacheWithFallback(const PathInfo& path,
|
|
const UnicodeString& desiredLocale,
|
|
UnicodeString& returnedLocale,
|
|
ResourceBundleCache* fgCache,
|
|
UErrorCode& error)
|
|
{
|
|
if(U_FAILURE(error))
|
|
return 0;
|
|
error = U_ZERO_ERROR;
|
|
|
|
LocaleFallbackIterator iterator(desiredLocale, kDefaultLocaleName, TRUE);
|
|
|
|
for(;;) {
|
|
const UHashtable* h = getFromCache(path, iterator.getLocale(), fgCache);
|
|
if(h != 0) {
|
|
returnedLocale = iterator.getLocale();
|
|
return h;
|
|
}
|
|
|
|
if(!iterator.nextLocale(error))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse the given file, if it hasn't been attempted already, and if
|
|
* it actually exists. Return true if a parse is attempted. Upon
|
|
* return, if the return value is true, the error code may be set as a
|
|
* result of a parse failure to a failing value. If the parse was
|
|
* successful, additional entries may have been created in the cache.
|
|
*/
|
|
bool_t
|
|
ResourceBundle::parseIfUnparsed(const PathInfo& path,
|
|
const UnicodeString& locale,
|
|
ResourceBundleCache* fgCache,
|
|
VisitedFileCache* fgVisitedFiles,
|
|
UErrorCode& error)
|
|
{
|
|
UnicodeString key(path.makeCacheKey(locale));
|
|
|
|
if(!fgVisitedFiles->wasVisited(key) && path.fileExists(locale)) {
|
|
parse(path, locale, addToCache, (void*)&path, fgCache, error);
|
|
{
|
|
Mutex lock;
|
|
fgVisitedFiles->markAsVisited(key);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Given a tag, try to retrieve the data for that tag. This method is
|
|
* semantically const, but may actually modify this object. All
|
|
* public API methods such as getString() rely on getDataForTag()
|
|
* ultimately. This method implements inheritance of data between
|
|
* locales.
|
|
*/
|
|
const ResourceBundleData*
|
|
ResourceBundle::getDataForTag(const char *tag,
|
|
UErrorCode& err) const
|
|
{
|
|
err = U_ZERO_ERROR; /* just to make sure there's no fallback/etc left over */
|
|
// Iterate over the kDataCount hashtables which may be associated with this
|
|
// bundle. At most we have kDataCount, but we may have as few as one.
|
|
for(int32_t i = 0; i < kDataCount; ++i) {
|
|
|
|
// First try to load up this hashtable, if it hasn't been loaded yet.
|
|
if(!fLoaded[i] && fData[i] == 0) {
|
|
ResourceBundle* nonconst = (ResourceBundle*)this;
|
|
nonconst->fLoaded[i] = TRUE;
|
|
if(fLocaleIterator->nextLocale(err)) {
|
|
UErrorCode getHashtableStatus = U_ZERO_ERROR;
|
|
|
|
nonconst->fDataStatus[i] = err;
|
|
nonconst->fData[i] =
|
|
nonconst->getHashtableForLocale(fLocaleIterator->getLocale(), getHashtableStatus);
|
|
}
|
|
}
|
|
|
|
|
|
if(fData[i] != 0) {
|
|
const ResourceBundleData* s =
|
|
(const ResourceBundleData*)uhash_get(fData[i],
|
|
UnicodeString(tag, "").hashCode() & 0x7FFFFFFF);
|
|
if(s != 0) {
|
|
err = fDataStatus[i]; /* restore the error from the original lookup. */
|
|
return s;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// cerr << "Failed to find tag " << tag << " in " << fPath << fRealLocaleID << fFilenameSuffix << endl;
|
|
// cerr << *this;
|
|
#endif
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::getString( const char *resourceTag,
|
|
UnicodeString& theString,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const UnicodeString* temp = getString(resourceTag, err);
|
|
if(U_SUCCESS(err))
|
|
theString = *temp;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::getString( const char *resourceTag,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return NULL;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == StringList::getStaticClassID()
|
|
&& ((StringList*)data)->fCount == 1) {
|
|
return &(((StringList*)data)->fStrings[0]);
|
|
}
|
|
else err = U_MISSING_RESOURCE_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::getStringArray( const char *resourceTag,
|
|
int32_t& count,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return 0;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == StringList::getStaticClassID()) {
|
|
count = ((StringList*)data)->fCount;
|
|
return ((StringList*)data)->fStrings;
|
|
}
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::getArrayItem( const char *resourceTag,
|
|
int32_t index,
|
|
UnicodeString& theArrayItem,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const UnicodeString* temp = getArrayItem(resourceTag, index, err);
|
|
if(U_SUCCESS(err))
|
|
theArrayItem = *temp;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::getArrayItem( const char *resourceTag,
|
|
int32_t index,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return NULL;
|
|
|
|
// Casting to unsigned turns a signed value into a large unsigned
|
|
// value. This allows us to do one comparison to check that 0 <=
|
|
// index < count, instead of two separate comparisons for each index
|
|
// check.
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == StringList::getStaticClassID()
|
|
&& (uint32_t)index < (uint32_t)((StringList*)data)->fCount) {
|
|
return &(((StringList*)data)->fStrings[index]);
|
|
}
|
|
else
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
const UnicodeString**
|
|
ResourceBundle::get2dArray(const char *resourceTag,
|
|
int32_t& rowCount,
|
|
int32_t& columnCount,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return 0;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == String2dList::getStaticClassID()) {
|
|
String2dList *list = (String2dList*)data;
|
|
rowCount = list->fRowCount;
|
|
columnCount = list->fColCount;
|
|
// Why is this cast required? It shouldn't be. [LIU]
|
|
return (const UnicodeString**)list->fStrings;
|
|
}
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::get2dArrayItem(const char *resourceTag,
|
|
int32_t rowIndex,
|
|
int32_t columnIndex,
|
|
UnicodeString& theArrayItem,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const UnicodeString* temp = get2dArrayItem(resourceTag, rowIndex,
|
|
columnIndex, err);
|
|
|
|
if(U_SUCCESS(err))
|
|
theArrayItem = *temp;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::get2dArrayItem(const char *resourceTag,
|
|
int32_t rowIndex,
|
|
int32_t columnIndex,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return NULL;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == String2dList::getStaticClassID()) {
|
|
String2dList *list = (String2dList*)data;
|
|
// Casting to unsigned turns a signed value into a large unsigned
|
|
// value. This allows us to do one comparison to check that 0 <=
|
|
// index < count, instead of two separate comparisons for each
|
|
// index check.
|
|
if(((uint32_t)rowIndex) < (uint32_t)(list->fRowCount)
|
|
&& ((uint32_t)columnIndex) < (uint32_t)(list->fColCount)) {
|
|
return &(list->fStrings[rowIndex][columnIndex]);
|
|
}
|
|
}
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
ResourceBundle::getTaggedArrayItem( const char *resourceTag,
|
|
const UnicodeString& itemTag,
|
|
UnicodeString& theArrayItem,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const UnicodeString* temp = getTaggedArrayItem(resourceTag, itemTag, err);
|
|
|
|
if(U_SUCCESS(err))
|
|
theArrayItem = *temp;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::getTaggedArrayItem( const char *resourceTag,
|
|
const UnicodeString& itemTag,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return NULL;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == TaggedList::getStaticClassID()) {
|
|
const UnicodeString* s = ((TaggedList*)data)->get(itemTag);
|
|
if(s != 0)
|
|
return s;
|
|
}
|
|
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
extern "C" void
|
|
T_ResourceBundle_getTaggedArrayUChars(const ResourceBundle* bundle,
|
|
const char *resourceTag,
|
|
UChar const** itemTags,
|
|
UChar const** items,
|
|
int32_t maxItems,
|
|
int32_t* numItems,
|
|
UErrorCode* err)
|
|
{
|
|
// this function is here solely because there seems to be no way to
|
|
// declare an extern "C" function as a friend of a class. So we
|
|
// have a function with ordinary C++ linkage that is a friend of
|
|
// ResourceBundle and does the work, and a hidden method with C
|
|
// linkage that calls it and is used by the C wrappers. Disgusting,
|
|
// isn't it? This was all rtg's idea. --jf 12/16/98
|
|
getTaggedArrayUCharsImplementation(bundle, resourceTag,
|
|
itemTags, items, maxItems,
|
|
*numItems, *err);
|
|
}
|
|
|
|
void
|
|
getTaggedArrayUCharsImplementation( const ResourceBundle* bundle,
|
|
const char *resourceTag,
|
|
UChar const** itemTags,
|
|
UChar const** items,
|
|
int32_t maxItems,
|
|
int32_t& numItems,
|
|
UErrorCode& err)
|
|
{
|
|
// this is here solely to support the C implementation of
|
|
// ResourceBundle. This function isn't defined as part of the API;
|
|
// The C wrappers know it's here and define it on their own. --jf
|
|
// 12/16/98
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const ResourceBundleData* data = bundle->getDataForTag(resourceTag, err);
|
|
if(U_FAILURE(err) || data == 0
|
|
|| data->getDynamicClassID() != TaggedList::getStaticClassID()) {
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return;
|
|
}
|
|
|
|
UHashtable* forEnumerationValues = ((TaggedList*)data)->fHashtableValues;
|
|
void* value;
|
|
|
|
numItems = 0;
|
|
int32_t pos = -1;
|
|
while(value = uhash_nextElement(forEnumerationValues, &pos)) {
|
|
if(numItems < maxItems) {
|
|
itemTags[numItems] =
|
|
((const UnicodeString*)uhash_get(((TaggedList*)data)->fHashtableKeys,
|
|
numItems+1))->getUChars();
|
|
items[numItems] = ((const UnicodeString*)value)->getUChars();
|
|
}
|
|
numItems++;
|
|
}
|
|
}
|
|
|
|
void
|
|
ResourceBundle::getTaggedArray( const char *resourceTag,
|
|
UnicodeString*& itemTags,
|
|
UnicodeString*& items,
|
|
int32_t& numItems,
|
|
UErrorCode& err) const
|
|
{
|
|
if(U_FAILURE(err))
|
|
return;
|
|
|
|
const ResourceBundleData* data = getDataForTag(resourceTag, err);
|
|
if(U_FAILURE(err) || data == 0
|
|
|| data->getDynamicClassID() != TaggedList::getStaticClassID()) {
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return;
|
|
}
|
|
|
|
// go through the resource once and count how many items there are
|
|
|
|
numItems = uhash_size(((TaggedList*)data)->fHashtableValues);
|
|
|
|
// now create the string arrays and go through the hash table again, this
|
|
// time copying the keys and values into the string arrays
|
|
itemTags = new UnicodeString[numItems];
|
|
items = new UnicodeString[numItems];
|
|
|
|
UHashtable* forEnumerationValues = ((TaggedList*)data)->fHashtableValues;
|
|
void* value;
|
|
|
|
numItems = 0;
|
|
int32_t pos = -1;
|
|
while(value = uhash_nextElement(forEnumerationValues, &pos)) {
|
|
itemTags[numItems] =
|
|
*((const UnicodeString*)uhash_get(((TaggedList*)data)->fHashtableKeys,
|
|
numItems+1));
|
|
items[numItems] = *((const UnicodeString*)value);
|
|
numItems++;
|
|
}
|
|
}
|
|
|
|
const char*
|
|
ResourceBundle::getVersionNumber() const
|
|
{
|
|
if(fVersionID == 0) {
|
|
// If the version ID has not been built yet, then do so. Retrieve
|
|
// the minor version from the file.
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
UnicodeString minor_version;
|
|
getString(kVersionTag, minor_version, status);
|
|
|
|
// Determine the length of of the final version string. This is
|
|
// the length of the major part + the length of the separator
|
|
// (==1) + the length of the minor part (+ 1 for the zero byte at
|
|
// the end).
|
|
int32_t len = icu_strlen(ICU_VERSION);
|
|
int32_t minor_len = 0;
|
|
if(U_SUCCESS(status) && minor_version.length() > 0)
|
|
minor_len = minor_version.length();
|
|
len += (minor_len > 0) ? minor_len : 1 /*==icu_strlen(kDefaultMinorVersion)*/;
|
|
++len; // Add length of separator
|
|
|
|
// Allocate the string, and build it up.
|
|
// + 1 for zero byte
|
|
((ResourceBundle*)this)->fVersionID = new char[1 + len];
|
|
|
|
icu_strcpy(fVersionID, ICU_VERSION);
|
|
icu_strcat(fVersionID, kVersionSeparator);
|
|
if(minor_len > 0) {
|
|
minor_version.extract(0, minor_len, fVersionID + len - minor_len);
|
|
fVersionID[len] = 0;
|
|
}
|
|
else {
|
|
icu_strcat(fVersionID, kDefaultMinorVersion);
|
|
}
|
|
}
|
|
return fVersionID;
|
|
}
|
|
|
|
const UnicodeString*
|
|
ResourceBundle::listInstalledLocales(const UnicodeString& path,
|
|
int32_t& numInstalledLocales)
|
|
{
|
|
const UnicodeString kDefaultSuffixString = UnicodeString(kDefaultSuffix,"");
|
|
|
|
|
|
const UHashtable* h = getFromCache(PathInfo(path, kDefaultSuffixString),
|
|
UnicodeString(kIndexLocaleName,""), fgUserCache);
|
|
|
|
if(h == 0) {
|
|
UErrorCode error = U_ZERO_ERROR;
|
|
if(parseIfUnparsed(PathInfo(path, kDefaultSuffixString),
|
|
UnicodeString(kIndexFilename,""), fgUserCache,
|
|
fgUserVisitedFiles, error)) {
|
|
h = getFromCache(PathInfo(path, kDefaultSuffixString),
|
|
UnicodeString(kIndexLocaleName,""), fgUserCache);
|
|
}
|
|
}
|
|
|
|
if(h != 0) {
|
|
UnicodeString ukIndexTag = UnicodeString(kIndexTag,"");
|
|
ResourceBundleData *data =
|
|
(ResourceBundleData*) uhash_get(h, ukIndexTag.hashCode() & 0x7FFFFFFF);
|
|
if(data != 0
|
|
&& data->getDynamicClassID() == StringList::getStaticClassID()) {
|
|
numInstalledLocales = ((StringList*)data)->fCount;
|
|
return ((StringList*)data)->fStrings;
|
|
}
|
|
}
|
|
|
|
numInstalledLocales = 0;
|
|
return 0;
|
|
}
|
|
|
|
extern "C" const UnicodeString**
|
|
T_ResourceBundle_listInstalledLocales(const char* path,
|
|
int32_t* numInstalledLocales)
|
|
{
|
|
// this is here solely to support the C implementation of Locale.
|
|
// This function isn't defined as part of the API; T_Locale knows
|
|
// it's here and defines it on its own. --rtg 11/28/98
|
|
|
|
return listInstalledLocalesImplementation(path, numInstalledLocales);
|
|
}
|
|
|
|
const UnicodeString**
|
|
listInstalledLocalesImplementation(const char* path,
|
|
int32_t* numInstalledLocales)
|
|
{
|
|
// this function is here solely because there seems to be no way to
|
|
// declare an extern "C" function as a friend of a class. So we
|
|
// have a function with ordinary C++ linkage that is a friend of
|
|
// ResourceBundle and does the work, and a hidden method with C
|
|
// linkage that calls it and is used by the C implementation of
|
|
// Locale. Disgusting, isn't it? --rtg 11/30/98
|
|
const UnicodeString* array = (ResourceBundle::listInstalledLocales(UnicodeString(path,""), *numInstalledLocales));
|
|
const UnicodeString** arrayOfPtrs = (const UnicodeString**) new UnicodeString*[*numInstalledLocales];
|
|
for(int i = 0; i < *numInstalledLocales; i++)
|
|
arrayOfPtrs[i] = &array[i];
|
|
return arrayOfPtrs;
|
|
}
|
|
|
|
int32_t
|
|
T_ResourceBundle_countArrayItemsImplementation(const ResourceBundle* resourceBundle,
|
|
const char* resourceKey,
|
|
UErrorCode& err)
|
|
{
|
|
if(U_FAILURE(err))
|
|
return 0;
|
|
|
|
if(!resourceKey) {
|
|
err = U_ILLEGAL_ARGUMENT_ERROR;
|
|
return 0;
|
|
}
|
|
const ResourceBundleData* data = resourceBundle->getDataForTag(resourceKey,
|
|
err);
|
|
if(U_FAILURE(err))
|
|
return 0;
|
|
|
|
UClassID rbkeyClassID = data->getDynamicClassID();
|
|
int32_t numItems = 0;
|
|
|
|
if(rbkeyClassID == StringList::getStaticClassID()) {
|
|
numItems = ((StringList*)data)->fCount;
|
|
}
|
|
else if(rbkeyClassID == TaggedList::getStaticClassID()) {
|
|
numItems = uhash_size(((TaggedList*)data)->fHashtableValues);
|
|
}
|
|
else if(rbkeyClassID == String2dList::getStaticClassID()) {
|
|
numItems = ((String2dList*)data)->fRowCount;
|
|
}
|
|
else {
|
|
err = U_MISSING_RESOURCE_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
return numItems;
|
|
}
|
|
|
|
|
|
extern "C" int32_t
|
|
T_ResourceBundle_countArrayItems(const ResourceBundle* resourceBundle,
|
|
const char* resourceKey,
|
|
UErrorCode* err)
|
|
{
|
|
return T_ResourceBundle_countArrayItemsImplementation(resourceBundle,
|
|
resourceKey,
|
|
*err);
|
|
}
|
|
|
|
/**
|
|
* Retrieve a ResourceBundle from the cache. Return NULL if not found.
|
|
*/
|
|
const UHashtable*
|
|
ResourceBundle::getFromCache(const PathInfo& path,
|
|
const UnicodeString& localeName,
|
|
ResourceBundleCache* fgCache)
|
|
{
|
|
UnicodeString keyname(path.makeHashkey(localeName));
|
|
Mutex lock;
|
|
|
|
return (const UHashtable*)
|
|
uhash_get(fgCache->hashTable, keyname.hashCode() & 0x7FFFFFFF);
|
|
}
|
|
|
|
/**
|
|
* Parse a file, storing the resource data in the cache.
|
|
*/
|
|
void
|
|
ResourceBundle::parse(const PathInfo& path,
|
|
const UnicodeString& locale,
|
|
Handler handler,
|
|
void *context,
|
|
ResourceBundleCache *fgCache,
|
|
UErrorCode& status)
|
|
{
|
|
FileStream *f;
|
|
UnicodeString localeName;
|
|
UHashtable *data;
|
|
|
|
if (U_FAILURE(status)) return;
|
|
|
|
f = path.openFile(locale);
|
|
if(f == 0) {
|
|
status = U_FILE_ACCESS_ERROR;
|
|
return;
|
|
}
|
|
|
|
/* Get the data from the compiled resource bundle file */
|
|
data = rb_parse(f, localeName, status);
|
|
|
|
/* Close the file */
|
|
T_FileStream_close(f);
|
|
|
|
if(U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
|
|
/* Invoke the handler function */
|
|
handler(localeName, data, context, fgCache);
|
|
}
|
|
|
|
void
|
|
ResourceBundle::addToCache(const UnicodeString& localeName,
|
|
UHashtable* hashtable,
|
|
void* context,
|
|
ResourceBundleCache* fgCache)
|
|
{
|
|
PathInfo *c = (PathInfo*)context;
|
|
UnicodeString keyName(c->makeHashkey(localeName));
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
Mutex lock;
|
|
if(uhash_get(fgCache->hashTable, keyName.hashCode() & 0x7FFFFFFF) == 0) {
|
|
uhash_putKey(fgCache->hashTable, keyName.hashCode() & 0x7FFFFFFF,
|
|
hashtable, &err);
|
|
}
|
|
}
|
|
|
|
const Locale &ResourceBundle::getLocale(void) const
|
|
{
|
|
return fRealLocale;
|
|
}
|
|
|
|
ResourceBundle::PathInfo::PathInfo()
|
|
: fWPrefix(NULL), fWSuffix(NULL)
|
|
{}
|
|
|
|
ResourceBundle::PathInfo::PathInfo(const PathInfo& source)
|
|
: fPrefix(source.fPrefix),
|
|
fSuffix(source.fSuffix),
|
|
fWPrefix(NULL), fWSuffix(NULL)
|
|
{
|
|
if(source.fWPrefix) {
|
|
fWPrefix = new wchar_t[icu_wcslen(source.fWPrefix)+1];
|
|
fWSuffix = new wchar_t[icu_wcslen(source.fWSuffix)+1];
|
|
icu_wcscpy(fWPrefix, source.fWPrefix);
|
|
icu_wcscpy(fWSuffix, source.fWSuffix);
|
|
}
|
|
}
|
|
|
|
ResourceBundle::PathInfo::PathInfo(const UnicodeString& path)
|
|
: fPrefix(path),
|
|
fWPrefix(NULL),
|
|
fWSuffix(NULL)
|
|
{}
|
|
|
|
ResourceBundle::PathInfo::PathInfo(const UnicodeString& path,
|
|
const UnicodeString& suffix)
|
|
: fPrefix(path),
|
|
fSuffix(suffix),
|
|
fWPrefix(NULL),
|
|
fWSuffix(NULL)
|
|
{}
|
|
|
|
ResourceBundle::PathInfo::PathInfo(const wchar_t* path,
|
|
const wchar_t* suffix)
|
|
: fPrefix(),
|
|
fSuffix(),
|
|
fWPrefix(NULL),
|
|
fWSuffix(NULL)
|
|
{
|
|
fWPrefix = new wchar_t[icu_wcslen(path)+1];
|
|
fWSuffix = new wchar_t[icu_wcslen(suffix)+1];
|
|
icu_wcscpy(fWPrefix, path);
|
|
icu_wcscpy(fWSuffix, suffix);
|
|
}
|
|
|
|
ResourceBundle::PathInfo::~PathInfo()
|
|
{
|
|
delete [] fWPrefix;
|
|
delete [] fWSuffix;
|
|
}
|
|
|
|
ResourceBundle::PathInfo&
|
|
ResourceBundle::PathInfo::operator=(const PathInfo& source)
|
|
{
|
|
if(this != &source) {
|
|
wchar_t* tempPref = NULL;
|
|
wchar_t* tempSuff = NULL;
|
|
if(source.fWPrefix) {
|
|
tempPref = new wchar_t[icu_wcslen(source.fWPrefix)+1];
|
|
tempSuff = new wchar_t[icu_wcslen(source.fWSuffix)+1];
|
|
icu_wcscpy(tempPref, source.fWPrefix);
|
|
icu_wcscpy(tempSuff, source.fWSuffix);
|
|
}
|
|
delete fWPrefix;
|
|
fWPrefix = tempPref;
|
|
delete fWSuffix;
|
|
fWSuffix = tempSuff;
|
|
fPrefix = source.fPrefix;
|
|
fSuffix = source.fSuffix;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool_t
|
|
ResourceBundle::PathInfo::fileExists(const UnicodeString& localeName) const
|
|
{
|
|
FileStream *temp = openFile(localeName);
|
|
if(temp) {
|
|
T_FileStream_close(temp);
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
UnicodeString
|
|
ResourceBundle::PathInfo::makeCacheKey(const UnicodeString& name) const
|
|
{
|
|
if(fWPrefix) {
|
|
UnicodeString key;
|
|
|
|
size_t prefSize = icu_wcstombs(NULL, fWPrefix, ((size_t)-1) >> 1);
|
|
size_t suffSize = icu_wcstombs(NULL, fWSuffix, ((size_t)-1) >> 1);
|
|
size_t tempSize = icu_max((int32_t)prefSize, (int32_t)suffSize);
|
|
char *temp = new char[tempSize + 1];
|
|
|
|
tempSize = icu_wcstombs(temp, fWPrefix, prefSize);
|
|
temp[tempSize] = 0;
|
|
key += UnicodeString(temp);
|
|
|
|
key += name;
|
|
|
|
tempSize = icu_wcstombs(temp, fWSuffix, suffSize);
|
|
temp[tempSize] = 0;
|
|
key += UnicodeString(temp);
|
|
|
|
delete [] temp;
|
|
|
|
return key;
|
|
}
|
|
else {
|
|
UnicodeString workingName(fPrefix);
|
|
workingName += name;
|
|
workingName += fSuffix;
|
|
|
|
return workingName;
|
|
}
|
|
}
|
|
|
|
UnicodeString
|
|
ResourceBundle::PathInfo::makeHashkey(const UnicodeString& localeName) const
|
|
{
|
|
if(fWPrefix) {
|
|
UnicodeString key(localeName);
|
|
|
|
key += kSeparator;
|
|
|
|
size_t prefSize = icu_wcstombs(NULL, fWPrefix, ((size_t)-1) >> 1);
|
|
size_t suffSize = icu_wcstombs(NULL, fWSuffix, ((size_t)-1) >> 1);
|
|
size_t tempSize = icu_max((int32_t)prefSize, (int32_t)suffSize);
|
|
char *temp = new char[tempSize + 1];
|
|
|
|
tempSize = icu_wcstombs(temp, fWSuffix, suffSize);
|
|
temp[tempSize] = 0;
|
|
key += UnicodeString(temp);
|
|
|
|
key += kSeparator;
|
|
|
|
tempSize = icu_wcstombs(temp, fWPrefix, prefSize);
|
|
temp[tempSize] = 0;
|
|
key += UnicodeString(temp);
|
|
|
|
delete [] temp;
|
|
|
|
return key;
|
|
}
|
|
else {
|
|
UnicodeString keyName = localeName;
|
|
keyName += kSeparator;
|
|
keyName += fSuffix;
|
|
keyName += kSeparator;
|
|
keyName += fPrefix;
|
|
return keyName;
|
|
}
|
|
}
|
|
|
|
FileStream*
|
|
ResourceBundle::PathInfo::openFile(const UnicodeString& localeName) const
|
|
{
|
|
if(fWPrefix) {
|
|
//use the wide version of fopen in TPlatformUtilities.
|
|
int32_t nameSize = localeName.length();
|
|
char* temp = new char[nameSize + 1];
|
|
localeName.extract(0, nameSize, temp);
|
|
temp[nameSize] = 0;
|
|
int32_t wideNameLen = icu_mbstowcs(NULL, temp, nameSize);
|
|
wchar_t* wideName = new wchar_t[wideNameLen + 1];
|
|
icu_mbstowcs(wideName, temp, nameSize);
|
|
wideName[wideNameLen] = 0;
|
|
delete [] temp;
|
|
|
|
size_t prefLen = icu_wcslen(fWPrefix);
|
|
size_t suffLen = icu_wcslen(fWSuffix);
|
|
|
|
int32_t destSize = prefLen + suffLen + wideNameLen;
|
|
wchar_t* dest = new wchar_t[destSize + 1];
|
|
icu_wcscpy(dest, fWPrefix);
|
|
dest[prefLen] = 0;
|
|
|
|
icu_wcscat(dest, wideName);
|
|
dest[prefLen + wideNameLen] = 0;
|
|
|
|
icu_wcscat(dest, fWSuffix);
|
|
dest[destSize] = 0;
|
|
|
|
int32_t fmodeLen = icu_mbstowcs(NULL, "rb", 2);
|
|
wchar_t* fmode = new wchar_t[fmodeLen + 1];
|
|
icu_mbstowcs(fmode, "rb", 2);
|
|
fmode[fmodeLen] = 0;
|
|
|
|
FileStream* result = T_FileStream_wopen(dest, fmode);
|
|
|
|
delete [] fmode;
|
|
delete [] dest;
|
|
delete [] wideName;
|
|
return result;
|
|
}
|
|
else {
|
|
//open file using standard char* routines
|
|
UnicodeString workingName(makeCacheKey(localeName));
|
|
int32_t size = workingName.length();
|
|
char* returnVal = new char[size + 1];
|
|
workingName.extract(0, size, returnVal, "");
|
|
returnVal[size] = 0;
|
|
FileStream* result = T_FileStream_open(returnVal, "rb");
|
|
delete [] returnVal;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
const UChar ResourceBundle::PathInfo::kSeparator = 0xF8FF;
|
|
|
|
//eof
|