resbund.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. **********************************************************************
  5. * Copyright (C) 1997-2013, International Business Machines
  6. * Corporation and others. All Rights Reserved.
  7. **********************************************************************
  8. *
  9. * File resbund.cpp
  10. *
  11. * Modification History:
  12. *
  13. * Date Name Description
  14. * 02/05/97 aliu Fixed bug in chopLocale. Added scanForLocaleInFile
  15. * based on code taken from scanForLocale. Added
  16. * constructor which attempts to read resource bundle
  17. * from a specific file, without searching other files.
  18. * 02/11/97 aliu Added UErrorCode return values to constructors. Fixed
  19. * infinite loops in scanForFile and scanForLocale.
  20. * Modified getRawResourceData to not delete storage in
  21. * localeData and resourceData which it doesn't own.
  22. * Added Mac compatibility #ifdefs for tellp() and
  23. * ios::nocreate.
  24. * 03/04/97 aliu Modified to use ExpandingDataSink objects instead of
  25. * the highly inefficient ostrstream objects.
  26. * 03/13/97 aliu Rewrote to load in entire resource bundle and store
  27. * it as a Hashtable of ResourceBundleData objects.
  28. * Added state table to govern parsing of files.
  29. * Modified to load locale index out of new file distinct
  30. * from default.txt.
  31. * 03/25/97 aliu Modified to support 2-d arrays, needed for timezone data.
  32. * Added support for custom file suffixes. Again, needed
  33. * to support timezone data. Improved error handling to
  34. * detect duplicate tags and subtags.
  35. * 04/07/97 aliu Fixed bug in getHashtableForLocale(). Fixed handling
  36. * of failing UErrorCode values on entry to API methods.
  37. * Fixed bugs in getArrayItem() for negative indices.
  38. * 04/29/97 aliu Update to use new Hashtable deletion protocol.
  39. * 05/06/97 aliu Flattened kTransitionTable for HP compiler.
  40. * Fixed usage of CharString.
  41. * 06/11/99 stephen Removed parsing of .txt files.
  42. * Reworked to use new binary format.
  43. * Cleaned up.
  44. * 06/14/99 stephen Removed methods taking a filename suffix.
  45. * 06/22/99 stephen Added missing T_FileStream_close in parse()
  46. * 11/09/99 weiv Added getLocale(), rewritten constructForLocale()
  47. * March 2000 weiv complete overhaul.
  48. ******************************************************************************
  49. */
  50. #include "unicode/utypes.h"
  51. #include "unicode/resbund.h"
  52. #include "cmemory.h"
  53. #include "mutex.h"
  54. #include "uassert.h"
  55. #include "umutex.h"
  56. #include "uresimp.h"
  57. U_NAMESPACE_BEGIN
  58. /*-----------------------------------------------------------------------------
  59. * Implementation Notes
  60. *
  61. * Resource bundles are read in once, and thereafter cached.
  62. * ResourceBundle statically keeps track of which files have been
  63. * read, so we are guaranteed that each file is read at most once.
  64. * Resource bundles can be loaded from different data directories and
  65. * will be treated as distinct, even if they are for the same locale.
  66. *
  67. * Resource bundles are lightweight objects, which have pointers to
  68. * one or more shared Hashtable objects containing all the data.
  69. * Copying would be cheap, but there is no copy constructor, since
  70. * there wasn't one in the original API.
  71. *
  72. * The ResourceBundle parsing mechanism is implemented as a transition
  73. * network, for easy maintenance and modification. The network is
  74. * implemented as a matrix (instead of in code) to make this even
  75. * easier. The matrix contains Transition objects. Each Transition
  76. * object describes a destination node and an action to take before
  77. * moving to the destination node. The source node is encoded by the
  78. * index of the object in the array that contains it. The pieces
  79. * needed to understand the transition network are the enums for node
  80. * IDs and actions, the parse() method, which walks through the
  81. * network and implements the actions, and the network itself. The
  82. * network guarantees certain conditions, for example, that a new
  83. * resource will not be closed until one has been opened first; or
  84. * that data will not be stored into a TaggedList until a TaggedList
  85. * has been created. Nonetheless, the code in parse() does some
  86. * consistency checks as it runs the network, and fails with an
  87. * U_INTERNAL_PROGRAM_ERROR if one of these checks fails. If the input
  88. * data has a bad format, an U_INVALID_FORMAT_ERROR is returned. If you
  89. * see an U_INTERNAL_PROGRAM_ERROR the transition matrix has a bug in
  90. * it.
  91. *
  92. * Old functionality of multiple locales in a single file is still
  93. * supported. For this reason, LOCALE names override FILE names. If
  94. * data for en_US is located in the en.txt file, once it is loaded,
  95. * the code will not care where it came from (other than remembering
  96. * which directory it came from). However, if there is an en_US
  97. * resource in en_US.txt, that will take precedence. There is no
  98. * limit to the number or type of resources that can be stored in a
  99. * file, however, files are only searched in a specific way. If
  100. * en_US_CA is requested, then first en_US_CA.txt is searched, then
  101. * en_US.txt, then en.txt, then default.txt. So it only makes sense
  102. * to put certain locales in certain files. In this example, it would
  103. * be logical to put en_US_CA, en_US, and en into the en.txt file,
  104. * since they would be found there if asked for. The extreme example
  105. * is to place all locale resources into default.txt, which should
  106. * also work.
  107. *
  108. * Inheritance is implemented. For example, xx_YY_zz inherits as
  109. * follows: xx_YY_zz, xx_YY, xx, default. Inheritance is implemented
  110. * as an array of hashtables. There will be from 1 to 4 hashtables in
  111. * the array.
  112. *
  113. * Fallback files are implemented. The fallback pattern is Language
  114. * Country Variant (LCV) -> LC -> L. Fallback is first done for the
  115. * requested locale. Then it is done for the default locale, as
  116. * returned by Locale::getDefault(). Then the special file
  117. * default.txt is searched for the default locale. The overall FILE
  118. * fallback path is LCV -> LC -> L -> dLCV -> dLC -> dL -> default.
  119. *
  120. * Note that although file name searching includes the default locale,
  121. * once a ResourceBundle object is constructed, the inheritance path
  122. * no longer includes the default locale. The path is LCV -> LC -> L
  123. * -> default.
  124. *
  125. * File parsing is lazy. Nothing is parsed unless it is called for by
  126. * someone. So when a ResourceBundle for xx_YY_zz is constructed,
  127. * only that locale is parsed (along with anything else in the same
  128. * file). Later, if the FooBar tag is asked for, and if it isn't
  129. * found in xx_YY_zz, then xx_YY.txt will be parsed and checked, and
  130. * so forth, until the chain is exhausted or the tag is found.
  131. *
  132. * Thread-safety is implemented around caches, both the cache that
  133. * stores all the resource data, and the cache that stores flags
  134. * indicating whether or not a file has been visited. These caches
  135. * delete their storage at static cleanup time, when the process
  136. * quits.
  137. *
  138. * ResourceBundle supports TableCollation as a special case. This
  139. * involves having special ResourceBundle objects which DO own their
  140. * data, since we don't want large collation rule strings in the
  141. * ResourceBundle cache (these are already cached in the
  142. * TableCollation cache). TableCollation files (.ctx files) have the
  143. * same format as normal resource data files, with a different
  144. * interpretation, from the standpoint of ResourceBundle. .ctx files
  145. * are loaded into otherwise ordinary ResourceBundle objects. They
  146. * don't inherit (that's implemented by TableCollation) and they own
  147. * their data (as mentioned above). However, they still support
  148. * possible multiple locales in a single .ctx file. (This is in
  149. * practice a bad idea, since you only want the one locale you're
  150. * looking for, and only one tag will be present
  151. * ("CollationElements"), so you don't need an inheritance chain of
  152. * multiple locales.) Up to 4 locale resources will be loaded from a
  153. * .ctx file; everything after the first 4 is ignored (parsed and
  154. * deleted). (Normal .txt files have no limit.) Instead of being
  155. * loaded into the cache, and then looked up as needed, the locale
  156. * resources are read straight into the ResourceBundle object.
  157. *
  158. * The Index, which used to reside in default.txt, has been moved to a
  159. * new file, index.txt. This file contains a slightly modified format
  160. * with the addition of the "InstalledLocales" tag; it looks like:
  161. *
  162. * Index {
  163. * InstalledLocales {
  164. * ar
  165. * ..
  166. * zh_TW
  167. * }
  168. * }
  169. */
  170. //-----------------------------------------------------------------------------
  171. UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ResourceBundle)
  172. ResourceBundle::ResourceBundle(UErrorCode &err)
  173. :UObject(), fLocale(nullptr)
  174. {
  175. fResource = ures_open(0, Locale::getDefault().getName(), &err);
  176. }
  177. ResourceBundle::ResourceBundle(const ResourceBundle &other)
  178. :UObject(other), fLocale(nullptr)
  179. {
  180. UErrorCode status = U_ZERO_ERROR;
  181. if (other.fResource) {
  182. fResource = ures_copyResb(0, other.fResource, &status);
  183. } else {
  184. /* Copying a bad resource bundle */
  185. fResource = nullptr;
  186. }
  187. }
  188. ResourceBundle::ResourceBundle(UResourceBundle *res, UErrorCode& err)
  189. :UObject(), fLocale(nullptr)
  190. {
  191. if (res) {
  192. fResource = ures_copyResb(0, res, &err);
  193. } else {
  194. /* Copying a bad resource bundle */
  195. fResource = nullptr;
  196. }
  197. }
  198. ResourceBundle::ResourceBundle(const char* path, const Locale& locale, UErrorCode& err)
  199. :UObject(), fLocale(nullptr)
  200. {
  201. fResource = ures_open(path, locale.getName(), &err);
  202. }
  203. ResourceBundle& ResourceBundle::operator=(const ResourceBundle& other)
  204. {
  205. if(this == &other) {
  206. return *this;
  207. }
  208. if(fResource != 0) {
  209. ures_close(fResource);
  210. fResource = nullptr;
  211. }
  212. if (fLocale != nullptr) {
  213. delete fLocale;
  214. fLocale = nullptr;
  215. }
  216. UErrorCode status = U_ZERO_ERROR;
  217. if (other.fResource) {
  218. fResource = ures_copyResb(0, other.fResource, &status);
  219. } else {
  220. /* Copying a bad resource bundle */
  221. fResource = nullptr;
  222. }
  223. return *this;
  224. }
  225. ResourceBundle::~ResourceBundle()
  226. {
  227. if(fResource != 0) {
  228. ures_close(fResource);
  229. }
  230. if(fLocale != nullptr) {
  231. delete(fLocale);
  232. }
  233. }
  234. ResourceBundle *
  235. ResourceBundle::clone() const {
  236. return new ResourceBundle(*this);
  237. }
  238. UnicodeString ResourceBundle::getString(UErrorCode& status) const {
  239. int32_t len = 0;
  240. const char16_t *r = ures_getString(fResource, &len, &status);
  241. return UnicodeString(true, r, len);
  242. }
  243. const uint8_t *ResourceBundle::getBinary(int32_t& len, UErrorCode& status) const {
  244. return ures_getBinary(fResource, &len, &status);
  245. }
  246. const int32_t *ResourceBundle::getIntVector(int32_t& len, UErrorCode& status) const {
  247. return ures_getIntVector(fResource, &len, &status);
  248. }
  249. uint32_t ResourceBundle::getUInt(UErrorCode& status) const {
  250. return ures_getUInt(fResource, &status);
  251. }
  252. int32_t ResourceBundle::getInt(UErrorCode& status) const {
  253. return ures_getInt(fResource, &status);
  254. }
  255. const char *ResourceBundle::getName() const {
  256. return ures_getName(fResource);
  257. }
  258. const char *ResourceBundle::getKey() const {
  259. return ures_getKey(fResource);
  260. }
  261. UResType ResourceBundle::getType() const {
  262. return ures_getType(fResource);
  263. }
  264. int32_t ResourceBundle::getSize() const {
  265. return ures_getSize(fResource);
  266. }
  267. UBool ResourceBundle::hasNext() const {
  268. return ures_hasNext(fResource);
  269. }
  270. void ResourceBundle::resetIterator() {
  271. ures_resetIterator(fResource);
  272. }
  273. ResourceBundle ResourceBundle::getNext(UErrorCode& status) {
  274. UResourceBundle r;
  275. ures_initStackObject(&r);
  276. ures_getNextResource(fResource, &r, &status);
  277. ResourceBundle res(&r, status);
  278. if (U_SUCCESS(status)) {
  279. ures_close(&r);
  280. }
  281. return res;
  282. }
  283. UnicodeString ResourceBundle::getNextString(UErrorCode& status) {
  284. int32_t len = 0;
  285. const char16_t* r = ures_getNextString(fResource, &len, 0, &status);
  286. return UnicodeString(true, r, len);
  287. }
  288. UnicodeString ResourceBundle::getNextString(const char ** key, UErrorCode& status) {
  289. int32_t len = 0;
  290. const char16_t* r = ures_getNextString(fResource, &len, key, &status);
  291. return UnicodeString(true, r, len);
  292. }
  293. ResourceBundle ResourceBundle::get(int32_t indexR, UErrorCode& status) const {
  294. UResourceBundle r;
  295. ures_initStackObject(&r);
  296. ures_getByIndex(fResource, indexR, &r, &status);
  297. ResourceBundle res(&r, status);
  298. if (U_SUCCESS(status)) {
  299. ures_close(&r);
  300. }
  301. return res;
  302. }
  303. UnicodeString ResourceBundle::getStringEx(int32_t indexS, UErrorCode& status) const {
  304. int32_t len = 0;
  305. const char16_t* r = ures_getStringByIndex(fResource, indexS, &len, &status);
  306. return UnicodeString(true, r, len);
  307. }
  308. ResourceBundle ResourceBundle::get(const char* key, UErrorCode& status) const {
  309. UResourceBundle r;
  310. ures_initStackObject(&r);
  311. ures_getByKey(fResource, key, &r, &status);
  312. ResourceBundle res(&r, status);
  313. if (U_SUCCESS(status)) {
  314. ures_close(&r);
  315. }
  316. return res;
  317. }
  318. ResourceBundle ResourceBundle::getWithFallback(const char* key, UErrorCode& status){
  319. UResourceBundle r;
  320. ures_initStackObject(&r);
  321. ures_getByKeyWithFallback(fResource, key, &r, &status);
  322. ResourceBundle res(&r, status);
  323. if(U_SUCCESS(status)){
  324. ures_close(&r);
  325. }
  326. return res;
  327. }
  328. UnicodeString ResourceBundle::getStringEx(const char* key, UErrorCode& status) const {
  329. int32_t len = 0;
  330. const char16_t* r = ures_getStringByKey(fResource, key, &len, &status);
  331. return UnicodeString(true, r, len);
  332. }
  333. const char*
  334. ResourceBundle::getVersionNumber() const
  335. {
  336. return ures_getVersionNumberInternal(fResource);
  337. }
  338. void ResourceBundle::getVersion(UVersionInfo versionInfo) const {
  339. ures_getVersion(fResource, versionInfo);
  340. }
  341. const Locale &ResourceBundle::getLocale() const {
  342. static UMutex gLocaleLock;
  343. Mutex lock(&gLocaleLock);
  344. if (fLocale != nullptr) {
  345. return *fLocale;
  346. }
  347. UErrorCode status = U_ZERO_ERROR;
  348. const char *localeName = ures_getLocaleInternal(fResource, &status);
  349. ResourceBundle *ncThis = const_cast<ResourceBundle *>(this);
  350. ncThis->fLocale = new Locale(localeName);
  351. return ncThis->fLocale != nullptr ? *ncThis->fLocale : Locale::getDefault();
  352. }
  353. const Locale ResourceBundle::getLocale(ULocDataLocaleType type, UErrorCode &status) const
  354. {
  355. return ures_getLocaleByType(fResource, type, &status);
  356. }
  357. U_NAMESPACE_END
  358. //eof