]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/coll.cpp
ICU-6.2.14.tar.gz
[apple/icu.git] / icuSources / i18n / coll.cpp
1 /*
2 ******************************************************************************
3 * Copyright (C) 1996-2004, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
6 */
7
8 /**
9 * File coll.cpp
10 *
11 * Created by: Helena Shih
12 *
13 * Modification History:
14 *
15 * Date Name Description
16 * 2/5/97 aliu Modified createDefault to load collation data from
17 * binary files when possible. Added related methods
18 * createCollationFromFile, chopLocale, createPathName.
19 * 2/11/97 aliu Added methods addToCache, findInCache, which implement
20 * a Collation cache. Modified createDefault to look in
21 * cache first, and also to store newly created Collation
22 * objects in the cache. Modified to not use gLocPath.
23 * 2/12/97 aliu Modified to create objects from RuleBasedCollator cache.
24 * Moved cache out of Collation class.
25 * 2/13/97 aliu Moved several methods out of this class and into
26 * RuleBasedCollator, with modifications. Modified
27 * createDefault() to call new RuleBasedCollator(Locale&)
28 * constructor. General clean up and documentation.
29 * 2/20/97 helena Added clone, operator==, operator!=, operator=, and copy
30 * constructor.
31 * 05/06/97 helena Added memory allocation error detection.
32 * 05/08/97 helena Added createInstance().
33 * 6/20/97 helena Java class name change.
34 * 04/23/99 stephen Removed EDecompositionMode, merged with
35 * Normalizer::EMode
36 * 11/23/9 srl Inlining of some critical functions
37 * 01/29/01 synwee Modified into a C++ wrapper calling C APIs (ucol.h)
38 */
39
40 #include "unicode/utypes.h"
41
42 #if !UCONFIG_NO_COLLATION
43
44 #include "unicode/coll.h"
45 #include "unicode/tblcoll.h"
46 #include "ucol_imp.h"
47 #include "cmemory.h"
48 #include "mutex.h"
49 #include "iculserv.h"
50 #include "ustrenum.h"
51 #include "ucln_in.h"
52
53 U_NAMESPACE_BEGIN
54 #if !UCONFIG_NO_SERVICE
55 U_NAMESPACE_END
56
57 static ICULocaleService* gService = NULL;
58 /**
59 * Release all static memory held by collator.
60 */
61 U_CDECL_BEGIN
62 static UBool U_CALLCONV collator_cleanup(void) {
63 if (gService) {
64 delete gService;
65 gService = NULL;
66 }
67 return TRUE;
68 }
69 U_CDECL_END
70
71 U_NAMESPACE_BEGIN
72
73 // ------------------------------------------
74 //
75 // Registration
76 //
77
78 //-------------------------------------------
79
80 CollatorFactory::~CollatorFactory() {}
81
82 //-------------------------------------------
83
84 UBool
85 CollatorFactory::visible(void) const {
86 return TRUE;
87 }
88
89 //-------------------------------------------
90
91 UnicodeString&
92 CollatorFactory::getDisplayName(const Locale& objectLocale,
93 const Locale& displayLocale,
94 UnicodeString& result)
95 {
96 return objectLocale.getDisplayName(displayLocale, result);
97 }
98
99 // -------------------------------------
100
101 class ICUCollatorFactory : public ICUResourceBundleFactory {
102 public:
103 ICUCollatorFactory(): ICUResourceBundleFactory(UnicodeString(U_ICUDATA_COLL, (char*)NULL)) { }
104 protected:
105 virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const;
106 };
107
108 UObject*
109 ICUCollatorFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const {
110 if (handlesKey(key, status)) {
111 const LocaleKey& lkey = (const LocaleKey&)key;
112 Locale loc;
113 // make sure the requested locale is correct
114 // default LocaleFactory uses currentLocale since that's the one vetted by handlesKey
115 // but for ICU rb resources we use the actual one since it will fallback again
116 lkey.canonicalLocale(loc);
117
118 return Collator::makeInstance(loc, status);
119 }
120 return NULL;
121 }
122
123 // -------------------------------------
124
125 class ICUCollatorService : public ICULocaleService {
126 public:
127 ICUCollatorService()
128 : ICULocaleService("Collator")
129 {
130 UErrorCode status = U_ZERO_ERROR;
131 registerFactory(new ICUCollatorFactory(), status);
132 }
133
134 virtual UObject* cloneInstance(UObject* instance) const {
135 return ((Collator*)instance)->clone();
136 }
137
138 virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* actualID, UErrorCode& status) const {
139 LocaleKey& lkey = (LocaleKey&)key;
140 if (actualID) {
141 // Ugly Hack Alert! We return an empty actualID to signal
142 // to callers that this is a default object, not a "real"
143 // service-created object. (TODO remove in 3.0) [aliu]
144 actualID->truncate(0);
145 }
146 Locale loc("");
147 lkey.canonicalLocale(loc);
148 return Collator::makeInstance(loc, status);
149 }
150
151 virtual UObject* getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const {
152 UnicodeString ar;
153 if (actualReturn == NULL) {
154 actualReturn = &ar;
155 }
156 Collator* result = (Collator*)ICULocaleService::getKey(key, actualReturn, status);
157 // Ugly Hack Alert! If the actualReturn length is zero, this
158 // means we got a default object, not a "real" service-created
159 // object. We don't call setLocales() on a default object,
160 // because that will overwrite its correct built-in locale
161 // metadata (valid & actual) with our incorrect data (all we
162 // have is the requested locale). (TODO remove in 3.0) [aliu]
163 if (result && actualReturn->length() > 0) {
164 const LocaleKey& lkey = (const LocaleKey&)key;
165 Locale canonicalLocale("");
166 Locale currentLocale("");
167
168 result->setLocales(lkey.canonicalLocale(canonicalLocale),
169 LocaleUtility::initLocaleFromName(*actualReturn, currentLocale));
170 }
171 return result;
172 }
173
174 virtual UBool isDefault() const {
175 return countFactories() == 1;
176 }
177 };
178
179 // -------------------------------------
180
181 class ICUCollatorService;
182
183 static ICULocaleService*
184 getService(void)
185 {
186 UBool needInit;
187 {
188 Mutex mutex;
189 needInit = (UBool)(gService == NULL);
190 }
191 if(needInit) {
192 ICULocaleService *newservice = new ICUCollatorService();
193 if(newservice) {
194 Mutex mutex;
195 if(gService == NULL) {
196 gService = newservice;
197 newservice = NULL;
198 }
199 }
200 if(newservice) {
201 delete newservice;
202 }
203 else {
204 #if !UCONFIG_NO_SERVICE
205 ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR, collator_cleanup);
206 #endif
207 }
208 }
209 return gService;
210 }
211
212 // -------------------------------------
213
214 static UBool
215 hasService(void)
216 {
217 Mutex mutex;
218 return gService != NULL;
219 }
220
221 // -------------------------------------
222
223 UCollator*
224 Collator::createUCollator(const char *loc,
225 UErrorCode *status)
226 {
227 UCollator *result = 0;
228 if (status && U_SUCCESS(*status) && hasService()) {
229 Locale desiredLocale(loc);
230 Collator *col = (Collator*)gService->get(desiredLocale, *status);
231 if (col && col->getDynamicClassID() == RuleBasedCollator::getStaticClassID()) {
232 RuleBasedCollator *rbc = (RuleBasedCollator *)col;
233 if (!rbc->dataIsOwned) {
234 result = ucol_safeClone(rbc->ucollator, NULL, NULL, status);
235 } else {
236 result = rbc->ucollator;
237 rbc->ucollator = NULL; // to prevent free on delete
238 }
239 }
240 delete col;
241 }
242 return result;
243 }
244 #endif /* UCONFIG_NO_SERVICE */
245
246 // Collator public methods -----------------------------------------------
247
248 Collator* U_EXPORT2 Collator::createInstance(UErrorCode& success)
249 {
250 return createInstance(Locale::getDefault(), success);
251 }
252
253 Collator* U_EXPORT2 Collator::createInstance(const Locale& desiredLocale,
254 UErrorCode& status)
255 {
256 if (U_FAILURE(status))
257 return 0;
258
259 #if !UCONFIG_NO_SERVICE
260 if (hasService()) {
261 Locale actualLoc;
262 Collator *result =
263 (Collator*)gService->get(desiredLocale, &actualLoc, status);
264 // Ugly Hack Alert! If the returned locale is empty (not root,
265 // but empty -- getName() == "") then that means the service
266 // returned a default object, not a "real" service object. In
267 // that case, the locale metadata (valid & actual) is setup
268 // correctly already, and we don't want to overwrite it. (TODO
269 // remove in 3.0) [aliu]
270 if (*actualLoc.getName() != 0) {
271 result->setLocales(desiredLocale, actualLoc);
272 }
273 return result;
274 }
275 #endif
276 return makeInstance(desiredLocale, status);
277 }
278
279
280 Collator* Collator::makeInstance(const Locale& desiredLocale,
281 UErrorCode& status)
282 {
283 // A bit of explanation is required here. Although in the current
284 // implementation
285 // Collator::createInstance() is just turning around and calling
286 // RuleBasedCollator(Locale&), this will not necessarily always be the
287 // case. For example, suppose we modify this code to handle a
288 // non-table-based Collator, such as that for Thai. In this case,
289 // createInstance() will have to be modified to somehow determine this fact
290 // (perhaps a field in the resource bundle). Then it can construct the
291 // non-table-based Collator in some other way, when it sees that it needs
292 // to.
293 // The specific caution is this: RuleBasedCollator(Locale&) will ALWAYS
294 // return a valid collation object, if the system if functioning properly.
295 // The reason is that it will fall back, use the default locale, and even
296 // use the built-in default collation rules. THEREFORE, createInstance()
297 // should in general ONLY CALL RuleBasedCollator(Locale&) IF IT KNOWS IN
298 // ADVANCE that the given locale's collation is properly implemented as a
299 // RuleBasedCollator.
300 // Currently, we don't do this...we always return a RuleBasedCollator,
301 // whether it is strictly correct to do so or not, without checking, because
302 // we currently have no way of checking.
303
304 RuleBasedCollator* collation = new RuleBasedCollator(desiredLocale,
305 status);
306 /* test for NULL */
307 if (collation == 0) {
308 status = U_MEMORY_ALLOCATION_ERROR;
309 return 0;
310 }
311 if (U_FAILURE(status))
312 {
313 delete collation;
314 collation = 0;
315 }
316 return collation;
317 }
318
319 #ifdef U_USE_COLLATION_OBSOLETE_2_6
320 // !!! dlf the following is obsolete, ignore registration for this
321
322 Collator *
323 Collator::createInstance(const Locale &loc,
324 UVersionInfo version,
325 UErrorCode &status)
326 {
327 Collator *collator;
328 UVersionInfo info;
329
330 collator=new RuleBasedCollator(loc, status);
331 /* test for NULL */
332 if (collator == 0) {
333 status = U_MEMORY_ALLOCATION_ERROR;
334 return 0;
335 }
336
337 if(U_SUCCESS(status)) {
338 collator->getVersion(info);
339 if(0!=uprv_memcmp(version, info, sizeof(UVersionInfo))) {
340 delete collator;
341 status=U_MISSING_RESOURCE_ERROR;
342 return 0;
343 }
344 }
345 return collator;
346 }
347 #endif
348
349 // implement deprecated, previously abstract method
350 Collator::EComparisonResult Collator::compare(const UnicodeString& source,
351 const UnicodeString& target) const
352 {
353 UErrorCode ec = U_ZERO_ERROR;
354 return (Collator::EComparisonResult)compare(source, target, ec);
355 }
356
357 // implement deprecated, previously abstract method
358 Collator::EComparisonResult Collator::compare(const UnicodeString& source,
359 const UnicodeString& target,
360 int32_t length) const
361 {
362 UErrorCode ec = U_ZERO_ERROR;
363 return (Collator::EComparisonResult)compare(source, target, length, ec);
364 }
365
366 // implement deprecated, previously abstract method
367 Collator::EComparisonResult Collator::compare(const UChar* source, int32_t sourceLength,
368 const UChar* target, int32_t targetLength)
369 const
370 {
371 UErrorCode ec = U_ZERO_ERROR;
372 return (Collator::EComparisonResult)compare(source, sourceLength, target, targetLength, ec);
373 }
374
375 UBool Collator::equals(const UnicodeString& source,
376 const UnicodeString& target) const
377 {
378 UErrorCode ec = U_ZERO_ERROR;
379 return (compare(source, target, ec) == UCOL_EQUAL);
380 }
381
382 UBool Collator::greaterOrEqual(const UnicodeString& source,
383 const UnicodeString& target) const
384 {
385 UErrorCode ec = U_ZERO_ERROR;
386 return (compare(source, target, ec) != UCOL_LESS);
387 }
388
389 UBool Collator::greater(const UnicodeString& source,
390 const UnicodeString& target) const
391 {
392 UErrorCode ec = U_ZERO_ERROR;
393 return (compare(source, target, ec) == UCOL_GREATER);
394 }
395
396 // this API ignores registered collators, since it returns an
397 // array of indefinite lifetime
398 const Locale* U_EXPORT2 Collator::getAvailableLocales(int32_t& count)
399 {
400 return Locale::getAvailableLocales(count);
401 }
402
403 UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale,
404 const Locale& displayLocale,
405 UnicodeString& name)
406 {
407 #if !UCONFIG_NO_SERVICE
408 if (hasService()) {
409 return gService->getDisplayName(objectLocale.getName(), name, displayLocale);
410 }
411 #endif
412 return objectLocale.getDisplayName(displayLocale, name);
413 }
414
415 UnicodeString& U_EXPORT2 Collator::getDisplayName(const Locale& objectLocale,
416 UnicodeString& name)
417 {
418 return getDisplayName(objectLocale, Locale::getDefault(), name);
419 }
420
421 /* This is useless information */
422 /*void Collator::getVersion(UVersionInfo versionInfo) const
423 {
424 if (versionInfo!=NULL)
425 uprv_memcpy(versionInfo, fVersion, U_MAX_VERSION_LENGTH);
426 }
427 */
428
429 // UCollator protected constructor destructor ----------------------------
430
431 /**
432 * Default constructor.
433 * Constructor is different from the old default Collator constructor.
434 * The task for determing the default collation strength and normalization mode
435 * is left to the child class.
436 */
437 Collator::Collator()
438 : UObject()
439 {
440 }
441
442 /**
443 * Constructor.
444 * Empty constructor, does not handle the arguments.
445 * This constructor is done for backward compatibility with 1.7 and 1.8.
446 * The task for handling the argument collation strength and normalization
447 * mode is left to the child class.
448 * @param collationStrength collation strength
449 * @param decompositionMode
450 * @deprecated 2.4 use the default constructor instead
451 */
452 Collator::Collator(UCollationStrength, UNormalizationMode )
453 : UObject()
454 {
455 }
456
457 Collator::~Collator()
458 {
459 }
460
461 Collator::Collator(const Collator &other)
462 : UObject(other)
463 {
464 }
465
466 UBool Collator::operator==(const Collator& other) const
467 {
468 return (UBool)(this == &other);
469 }
470
471 UBool Collator::operator!=(const Collator& other) const
472 {
473 return (UBool)!(*this == other);
474 }
475
476 int32_t U_EXPORT2 Collator::getBound(const uint8_t *source,
477 int32_t sourceLength,
478 UColBoundMode boundType,
479 uint32_t noOfLevels,
480 uint8_t *result,
481 int32_t resultLength,
482 UErrorCode &status)
483 {
484 return ucol_getBound(source, sourceLength, boundType, noOfLevels, result, resultLength, &status);
485 }
486
487 void
488 Collator::setLocales(const Locale& /* requestedLocale */, const Locale& /* validLocale */) {
489 }
490
491 UnicodeSet *Collator::getTailoredSet(UErrorCode &status) const
492 {
493 if(U_FAILURE(status)) {
494 return NULL;
495 }
496 // everything can be changed
497 return new UnicodeSet(0, 0x10FFFF);
498 }
499
500 // -------------------------------------
501
502 #if !UCONFIG_NO_SERVICE
503 URegistryKey U_EXPORT2
504 Collator::registerInstance(Collator* toAdopt, const Locale& locale, UErrorCode& status)
505 {
506 if (U_SUCCESS(status)) {
507 return getService()->registerInstance(toAdopt, locale, status);
508 }
509 return NULL;
510 }
511
512 // -------------------------------------
513
514 class CFactory : public LocaleKeyFactory {
515 private:
516 CollatorFactory* _delegate;
517 Hashtable* _ids;
518
519 public:
520 CFactory(CollatorFactory* delegate, UErrorCode& status)
521 : LocaleKeyFactory(delegate->visible() ? VISIBLE : INVISIBLE)
522 , _delegate(delegate)
523 , _ids(NULL)
524 {
525 if (U_SUCCESS(status)) {
526 int32_t count = 0;
527 _ids = new Hashtable(status);
528 if (_ids) {
529 const UnicodeString * idlist = _delegate->getSupportedIDs(count, status);
530 for (int i = 0; i < count; ++i) {
531 _ids->put(idlist[i], (void*)this, status);
532 if (U_FAILURE(status)) {
533 delete _ids;
534 _ids = NULL;
535 return;
536 }
537 }
538 } else {
539 status = U_MEMORY_ALLOCATION_ERROR;
540 }
541 }
542 }
543
544 virtual ~CFactory()
545 {
546 delete _delegate;
547 delete _ids;
548 }
549
550 virtual UObject* create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const;
551
552 protected:
553 virtual const Hashtable* getSupportedIDs(UErrorCode& status) const
554 {
555 if (U_SUCCESS(status)) {
556 return _ids;
557 }
558 return NULL;
559 }
560
561 virtual UnicodeString&
562 getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const;
563 };
564
565 UObject*
566 CFactory::create(const ICUServiceKey& key, const ICUService* /* service */, UErrorCode& status) const
567 {
568 if (handlesKey(key, status)) {
569 const LocaleKey& lkey = (const LocaleKey&)key;
570 Locale validLoc;
571 lkey.currentLocale(validLoc);
572 return _delegate->createCollator(validLoc);
573 }
574 return NULL;
575 }
576
577 UnicodeString&
578 CFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const
579 {
580 if ((_coverage & 0x1) == 0) {
581 UErrorCode status = U_ZERO_ERROR;
582 const Hashtable* ids = getSupportedIDs(status);
583 if (ids && (ids->get(id) != NULL)) {
584 Locale loc;
585 LocaleUtility::initLocaleFromName(id, loc);
586 return _delegate->getDisplayName(loc, locale, result);
587 }
588 }
589 result.setToBogus();
590 return result;
591 }
592
593 URegistryKey U_EXPORT2
594 Collator::registerFactory(CollatorFactory* toAdopt, UErrorCode& status)
595 {
596 if (U_SUCCESS(status)) {
597 CFactory* f = new CFactory(toAdopt, status);
598 if (f) {
599 return getService()->registerFactory(f, status);
600 }
601 status = U_MEMORY_ALLOCATION_ERROR;
602 }
603 return NULL;
604 }
605
606 // -------------------------------------
607
608 UBool U_EXPORT2
609 Collator::unregister(URegistryKey key, UErrorCode& status)
610 {
611 if (U_SUCCESS(status)) {
612 if (hasService()) {
613 return gService->unregister(key, status);
614 }
615 status = U_ILLEGAL_ARGUMENT_ERROR;
616 }
617 return FALSE;
618 }
619
620 // -------------------------------------
621
622 StringEnumeration* U_EXPORT2
623 Collator::getAvailableLocales(void)
624 {
625 return getService()->getAvailableLocales();
626 }
627 #endif /* UCONFIG_NO_SERVICE */
628
629 StringEnumeration* U_EXPORT2
630 Collator::getKeywords(UErrorCode& status) {
631 // This is a wrapper over ucol_getKeywords
632 UEnumeration* uenum = ucol_getKeywords(&status);
633 if (U_FAILURE(status)) {
634 uenum_close(uenum);
635 return NULL;
636 }
637 return new UStringEnumeration(uenum);
638 }
639
640 StringEnumeration* U_EXPORT2
641 Collator::getKeywordValues(const char *keyword, UErrorCode& status) {
642 // This is a wrapper over ucol_getKeywordValues
643 UEnumeration* uenum = ucol_getKeywordValues(keyword, &status);
644 if (U_FAILURE(status)) {
645 uenum_close(uenum);
646 return NULL;
647 }
648 return new UStringEnumeration(uenum);
649 }
650
651 Locale U_EXPORT2
652 Collator::getFunctionalEquivalent(const char* keyword, const Locale& locale,
653 UBool& isAvailable, UErrorCode& status) {
654 // This is a wrapper over ucol_getFunctionalEquivalent
655 char loc[ULOC_FULLNAME_CAPACITY];
656 /*int32_t len =*/ ucol_getFunctionalEquivalent(loc, sizeof(loc),
657 keyword, locale.getName(), &isAvailable, &status);
658 if (U_FAILURE(status)) {
659 *loc = 0; // root
660 }
661 return Locale::createFromName(loc);
662 }
663
664 // UCollator private data members ----------------------------------------
665
666 /* This is useless information */
667 /*const UVersionInfo Collator::fVersion = {1, 1, 0, 0};*/
668
669 // -------------------------------------
670
671 U_NAMESPACE_END
672
673 #endif /* #if !UCONFIG_NO_COLLATION */
674
675 /* eof */