2 ******************************************************************************
3 * Copyright (C) 1996-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
11 * Created by: Helena Shih
13 * Modification History:
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
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
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)
40 #include "unicode/utypes.h"
42 #if !UCONFIG_NO_COLLATION
44 #include "unicode/coll.h"
45 #include "unicode/tblcoll.h"
55 static icu::Locale
* availableLocaleList
= NULL
;
56 static int32_t availableLocaleListCount
;
57 static icu::ICULocaleService
* gService
= NULL
;
60 * Release all static memory held by collator.
63 static UBool U_CALLCONV
collator_cleanup(void) {
64 #if !UCONFIG_NO_SERVICE
70 if (availableLocaleList
) {
71 delete []availableLocaleList
;
72 availableLocaleList
= NULL
;
74 availableLocaleListCount
= 0;
83 #if !UCONFIG_NO_SERVICE
85 // ------------------------------------------
90 //-------------------------------------------
92 CollatorFactory::~CollatorFactory() {}
94 //-------------------------------------------
97 CollatorFactory::visible(void) const {
101 //-------------------------------------------
104 CollatorFactory::getDisplayName(const Locale
& objectLocale
,
105 const Locale
& displayLocale
,
106 UnicodeString
& result
)
108 return objectLocale
.getDisplayName(displayLocale
, result
);
111 // -------------------------------------
113 class ICUCollatorFactory
: public ICUResourceBundleFactory
{
115 ICUCollatorFactory() : ICUResourceBundleFactory(UnicodeString(U_ICUDATA_COLL
, -1, US_INV
)) { }
116 virtual ~ICUCollatorFactory();
118 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const;
121 ICUCollatorFactory::~ICUCollatorFactory() {}
124 ICUCollatorFactory::create(const ICUServiceKey
& key
, const ICUService
* /* service */, UErrorCode
& status
) const {
125 if (handlesKey(key
, status
)) {
126 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
128 // make sure the requested locale is correct
129 // default LocaleFactory uses currentLocale since that's the one vetted by handlesKey
130 // but for ICU rb resources we use the actual one since it will fallback again
131 lkey
.canonicalLocale(loc
);
133 return Collator::makeInstance(loc
, status
);
138 // -------------------------------------
140 class ICUCollatorService
: public ICULocaleService
{
143 : ICULocaleService(UNICODE_STRING_SIMPLE("Collator"))
145 UErrorCode status
= U_ZERO_ERROR
;
146 registerFactory(new ICUCollatorFactory(), status
);
149 virtual ~ICUCollatorService();
151 virtual UObject
* cloneInstance(UObject
* instance
) const {
152 return ((Collator
*)instance
)->clone();
155 virtual UObject
* handleDefault(const ICUServiceKey
& key
, UnicodeString
* actualID
, UErrorCode
& status
) const {
156 LocaleKey
& lkey
= (LocaleKey
&)key
;
158 // Ugly Hack Alert! We return an empty actualID to signal
159 // to callers that this is a default object, not a "real"
160 // service-created object. (TODO remove in 3.0) [aliu]
161 actualID
->truncate(0);
164 lkey
.canonicalLocale(loc
);
165 return Collator::makeInstance(loc
, status
);
168 virtual UObject
* getKey(ICUServiceKey
& key
, UnicodeString
* actualReturn
, UErrorCode
& status
) const {
170 if (actualReturn
== NULL
) {
173 Collator
* result
= (Collator
*)ICULocaleService::getKey(key
, actualReturn
, status
);
174 // Ugly Hack Alert! If the actualReturn length is zero, this
175 // means we got a default object, not a "real" service-created
176 // object. We don't call setLocales() on a default object,
177 // because that will overwrite its correct built-in locale
178 // metadata (valid & actual) with our incorrect data (all we
179 // have is the requested locale). (TODO remove in 3.0) [aliu]
180 if (result
&& actualReturn
->length() > 0) {
181 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
182 Locale
canonicalLocale("");
183 Locale
currentLocale("");
185 LocaleUtility::initLocaleFromName(*actualReturn
, currentLocale
);
186 result
->setLocales(lkey
.canonicalLocale(canonicalLocale
), currentLocale
, currentLocale
);
191 virtual UBool
isDefault() const {
192 return countFactories() == 1;
196 ICUCollatorService::~ICUCollatorService() {}
198 // -------------------------------------
200 static ICULocaleService
*
204 UMTX_CHECK(NULL
, (UBool
)(gService
== NULL
), needInit
);
206 ICULocaleService
*newservice
= new ICUCollatorService();
209 if(gService
== NULL
) {
210 gService
= newservice
;
219 ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR
, collator_cleanup
);
225 // -------------------------------------
231 UMTX_CHECK(NULL
, gService
!= NULL
, retVal
);
235 // -------------------------------------
238 Collator::createUCollator(const char *loc
,
241 UCollator
*result
= 0;
242 if (status
&& U_SUCCESS(*status
) && hasService()) {
243 Locale
desiredLocale(loc
);
244 Collator
*col
= (Collator
*)gService
->get(desiredLocale
, *status
);
245 RuleBasedCollator
*rbc
;
246 if (col
&& (rbc
= dynamic_cast<RuleBasedCollator
*>(col
))) {
247 if (!rbc
->dataIsOwned
) {
248 result
= ucol_safeClone(rbc
->ucollator
, NULL
, NULL
, status
);
250 result
= rbc
->ucollator
;
251 rbc
->ucollator
= NULL
; // to prevent free on delete
254 // should go in a function- ucol_initDelegate(delegate)
255 result
= (UCollator
*)uprv_malloc(sizeof(UCollator
));
257 *status
= U_MEMORY_ALLOCATION_ERROR
;
259 uprv_memset(result
, 0, sizeof(UCollator
));
260 result
->delegate
= col
;
261 result
->freeOnClose
= TRUE
; // do free on close.
262 col
= NULL
; // to prevent free on delete.
269 #endif /* UCONFIG_NO_SERVICE */
271 static UBool
isAvailableLocaleListInitialized(UErrorCode
&status
) {
272 // for now, there is a hardcoded list, so just walk through that list and set it up.
274 UMTX_CHECK(NULL
, availableLocaleList
== NULL
, needInit
);
277 UResourceBundle
*index
= NULL
;
278 UResourceBundle installed
;
283 ures_initStackObject(&installed
);
284 index
= ures_openDirect(U_ICUDATA_COLL
, "res_index", &status
);
285 ures_getByKey(index
, "InstalledLocales", &installed
, &status
);
287 if(U_SUCCESS(status
)) {
288 localeCount
= ures_getSize(&installed
);
289 temp
= new Locale
[localeCount
];
292 ures_resetIterator(&installed
);
293 while(ures_hasNext(&installed
)) {
294 const char *tempKey
= NULL
;
295 ures_getNextString(&installed
, NULL
, &tempKey
, &status
);
296 temp
[i
++] = Locale(tempKey
);
300 if (availableLocaleList
== NULL
)
302 availableLocaleListCount
= localeCount
;
303 availableLocaleList
= temp
;
305 ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR
, collator_cleanup
);
315 ures_close(&installed
);
322 // Collator public methods -----------------------------------------------
324 Collator
* U_EXPORT2
Collator::createInstance(UErrorCode
& success
)
326 return createInstance(Locale::getDefault(), success
);
329 Collator
* U_EXPORT2
Collator::createInstance(const Locale
& desiredLocale
,
332 if (U_FAILURE(status
))
335 #if !UCONFIG_NO_SERVICE
339 (Collator
*)gService
->get(desiredLocale
, &actualLoc
, status
);
341 // Ugly Hack Alert! If the returned locale is empty (not root,
342 // but empty -- getName() == "") then that means the service
343 // returned a default object, not a "real" service object. In
344 // that case, the locale metadata (valid & actual) is setup
345 // correctly already, and we don't want to overwrite it. (TODO
346 // remove in 3.0) [aliu]
347 if (*actualLoc
.getName() != 0) {
348 result
->setLocales(desiredLocale
, actualLoc
, actualLoc
);
353 return makeInstance(desiredLocale
, status
);
357 Collator
* Collator::makeInstance(const Locale
& desiredLocale
,
360 // A bit of explanation is required here. Although in the current
362 // Collator::createInstance() is just turning around and calling
363 // RuleBasedCollator(Locale&), this will not necessarily always be the
364 // case. For example, suppose we modify this code to handle a
365 // non-table-based Collator, such as that for Thai. In this case,
366 // createInstance() will have to be modified to somehow determine this fact
367 // (perhaps a field in the resource bundle). Then it can construct the
368 // non-table-based Collator in some other way, when it sees that it needs
370 // The specific caution is this: RuleBasedCollator(Locale&) will ALWAYS
371 // return a valid collation object, if the system is functioning properly.
372 // The reason is that it will fall back, use the default locale, and even
373 // use the built-in default collation rules. THEREFORE, createInstance()
374 // should in general ONLY CALL RuleBasedCollator(Locale&) IF IT KNOWS IN
375 // ADVANCE that the given locale's collation is properly implemented as a
376 // RuleBasedCollator.
377 // Currently, we don't do this...we always return a RuleBasedCollator,
378 // whether it is strictly correct to do so or not, without checking, because
379 // we currently have no way of checking.
381 RuleBasedCollator
* collation
= new RuleBasedCollator(desiredLocale
,
384 if (collation
== 0) {
385 status
= U_MEMORY_ALLOCATION_ERROR
;
388 if (U_FAILURE(status
))
396 #ifdef U_USE_COLLATION_OBSOLETE_2_6
397 // !!! dlf the following is obsolete, ignore registration for this
400 Collator::createInstance(const Locale
&loc
,
401 UVersionInfo version
,
407 collator
=new RuleBasedCollator(loc
, status
);
410 status
= U_MEMORY_ALLOCATION_ERROR
;
414 if(U_SUCCESS(status
)) {
415 collator
->getVersion(info
);
416 if(0!=uprv_memcmp(version
, info
, sizeof(UVersionInfo
))) {
418 status
=U_MISSING_RESOURCE_ERROR
;
426 // implement deprecated, previously abstract method
427 Collator::EComparisonResult
Collator::compare(const UnicodeString
& source
,
428 const UnicodeString
& target
) const
430 UErrorCode ec
= U_ZERO_ERROR
;
431 return (Collator::EComparisonResult
)compare(source
, target
, ec
);
434 // implement deprecated, previously abstract method
435 Collator::EComparisonResult
Collator::compare(const UnicodeString
& source
,
436 const UnicodeString
& target
,
437 int32_t length
) const
439 UErrorCode ec
= U_ZERO_ERROR
;
440 return (Collator::EComparisonResult
)compare(source
, target
, length
, ec
);
443 // implement deprecated, previously abstract method
444 Collator::EComparisonResult
Collator::compare(const UChar
* source
, int32_t sourceLength
,
445 const UChar
* target
, int32_t targetLength
)
448 UErrorCode ec
= U_ZERO_ERROR
;
449 return (Collator::EComparisonResult
)compare(source
, sourceLength
, target
, targetLength
, ec
);
452 UCollationResult
Collator::compare(UCharIterator
&/*sIter*/,
453 UCharIterator
&/*tIter*/,
454 UErrorCode
&status
) const {
455 if(U_SUCCESS(status
)) {
456 // Not implemented in the base class.
457 status
= U_UNSUPPORTED_ERROR
;
462 UCollationResult
Collator::compareUTF8(const StringPiece
&source
,
463 const StringPiece
&target
,
464 UErrorCode
&status
) const {
465 if(U_FAILURE(status
)) {
468 UCharIterator sIter
, tIter
;
469 uiter_setUTF8(&sIter
, source
.data(), source
.length());
470 uiter_setUTF8(&tIter
, target
.data(), target
.length());
471 return compare(sIter
, tIter
, status
);
474 UBool
Collator::equals(const UnicodeString
& source
,
475 const UnicodeString
& target
) const
477 UErrorCode ec
= U_ZERO_ERROR
;
478 return (compare(source
, target
, ec
) == UCOL_EQUAL
);
481 UBool
Collator::greaterOrEqual(const UnicodeString
& source
,
482 const UnicodeString
& target
) const
484 UErrorCode ec
= U_ZERO_ERROR
;
485 return (compare(source
, target
, ec
) != UCOL_LESS
);
488 UBool
Collator::greater(const UnicodeString
& source
,
489 const UnicodeString
& target
) const
491 UErrorCode ec
= U_ZERO_ERROR
;
492 return (compare(source
, target
, ec
) == UCOL_GREATER
);
495 // this API ignores registered collators, since it returns an
496 // array of indefinite lifetime
497 const Locale
* U_EXPORT2
Collator::getAvailableLocales(int32_t& count
)
499 UErrorCode status
= U_ZERO_ERROR
;
500 Locale
*result
= NULL
;
502 if (isAvailableLocaleListInitialized(status
))
504 result
= availableLocaleList
;
505 count
= availableLocaleListCount
;
510 UnicodeString
& U_EXPORT2
Collator::getDisplayName(const Locale
& objectLocale
,
511 const Locale
& displayLocale
,
514 #if !UCONFIG_NO_SERVICE
516 UnicodeString locNameStr
;
517 LocaleUtility::initNameFromLocale(objectLocale
, locNameStr
);
518 return gService
->getDisplayName(locNameStr
, name
, displayLocale
);
521 return objectLocale
.getDisplayName(displayLocale
, name
);
524 UnicodeString
& U_EXPORT2
Collator::getDisplayName(const Locale
& objectLocale
,
527 return getDisplayName(objectLocale
, Locale::getDefault(), name
);
530 /* This is useless information */
531 /*void Collator::getVersion(UVersionInfo versionInfo) const
533 if (versionInfo!=NULL)
534 uprv_memcpy(versionInfo, fVersion, U_MAX_VERSION_LENGTH);
538 // UCollator protected constructor destructor ----------------------------
541 * Default constructor.
542 * Constructor is different from the old default Collator constructor.
543 * The task for determing the default collation strength and normalization mode
544 * is left to the child class.
553 * Empty constructor, does not handle the arguments.
554 * This constructor is done for backward compatibility with 1.7 and 1.8.
555 * The task for handling the argument collation strength and normalization
556 * mode is left to the child class.
557 * @param collationStrength collation strength
558 * @param decompositionMode
559 * @deprecated 2.4 use the default constructor instead
561 Collator::Collator(UCollationStrength
, UNormalizationMode
)
566 Collator::~Collator()
570 Collator::Collator(const Collator
&other
)
575 UBool
Collator::operator==(const Collator
& other
) const
577 return (UBool
)(this == &other
);
580 UBool
Collator::operator!=(const Collator
& other
) const
582 return (UBool
)!(*this == other
);
585 int32_t U_EXPORT2
Collator::getBound(const uint8_t *source
,
586 int32_t sourceLength
,
587 UColBoundMode boundType
,
590 int32_t resultLength
,
593 return ucol_getBound(source
, sourceLength
, boundType
, noOfLevels
, result
, resultLength
, &status
);
597 Collator::setLocales(const Locale
& /* requestedLocale */, const Locale
& /* validLocale */, const Locale
& /*actualLocale*/) {
600 UnicodeSet
*Collator::getTailoredSet(UErrorCode
&status
) const
602 if(U_FAILURE(status
)) {
605 // everything can be changed
606 return new UnicodeSet(0, 0x10FFFF);
609 // -------------------------------------
611 #if !UCONFIG_NO_SERVICE
612 URegistryKey U_EXPORT2
613 Collator::registerInstance(Collator
* toAdopt
, const Locale
& locale
, UErrorCode
& status
)
615 if (U_SUCCESS(status
)) {
616 return getService()->registerInstance(toAdopt
, locale
, status
);
621 // -------------------------------------
623 class CFactory
: public LocaleKeyFactory
{
625 CollatorFactory
* _delegate
;
629 CFactory(CollatorFactory
* delegate
, UErrorCode
& status
)
630 : LocaleKeyFactory(delegate
->visible() ? VISIBLE
: INVISIBLE
)
631 , _delegate(delegate
)
634 if (U_SUCCESS(status
)) {
636 _ids
= new Hashtable(status
);
638 const UnicodeString
* idlist
= _delegate
->getSupportedIDs(count
, status
);
639 for (int i
= 0; i
< count
; ++i
) {
640 _ids
->put(idlist
[i
], (void*)this, status
);
641 if (U_FAILURE(status
)) {
648 status
= U_MEMORY_ALLOCATION_ERROR
;
655 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const;
658 virtual const Hashtable
* getSupportedIDs(UErrorCode
& status
) const
660 if (U_SUCCESS(status
)) {
666 virtual UnicodeString
&
667 getDisplayName(const UnicodeString
& id
, const Locale
& locale
, UnicodeString
& result
) const;
670 CFactory::~CFactory()
677 CFactory::create(const ICUServiceKey
& key
, const ICUService
* /* service */, UErrorCode
& status
) const
679 if (handlesKey(key
, status
)) {
680 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
682 lkey
.currentLocale(validLoc
);
683 return _delegate
->createCollator(validLoc
);
689 CFactory::getDisplayName(const UnicodeString
& id
, const Locale
& locale
, UnicodeString
& result
) const
691 if ((_coverage
& 0x1) == 0) {
692 UErrorCode status
= U_ZERO_ERROR
;
693 const Hashtable
* ids
= getSupportedIDs(status
);
694 if (ids
&& (ids
->get(id
) != NULL
)) {
696 LocaleUtility::initLocaleFromName(id
, loc
);
697 return _delegate
->getDisplayName(loc
, locale
, result
);
704 URegistryKey U_EXPORT2
705 Collator::registerFactory(CollatorFactory
* toAdopt
, UErrorCode
& status
)
707 if (U_SUCCESS(status
)) {
708 CFactory
* f
= new CFactory(toAdopt
, status
);
710 return getService()->registerFactory(f
, status
);
712 status
= U_MEMORY_ALLOCATION_ERROR
;
717 // -------------------------------------
720 Collator::unregister(URegistryKey key
, UErrorCode
& status
)
722 if (U_SUCCESS(status
)) {
724 return gService
->unregister(key
, status
);
726 status
= U_ILLEGAL_ARGUMENT_ERROR
;
730 #endif /* UCONFIG_NO_SERVICE */
732 class CollationLocaleListEnumeration
: public StringEnumeration
{
736 static UClassID U_EXPORT2
getStaticClassID(void);
737 virtual UClassID
getDynamicClassID(void) const;
739 CollationLocaleListEnumeration()
742 // The global variables should already be initialized.
743 //isAvailableLocaleListInitialized(status);
746 virtual ~CollationLocaleListEnumeration();
748 virtual StringEnumeration
* clone() const
750 CollationLocaleListEnumeration
*result
= new CollationLocaleListEnumeration();
752 result
->index
= index
;
757 virtual int32_t count(UErrorCode
&/*status*/) const {
758 return availableLocaleListCount
;
761 virtual const char* next(int32_t* resultLength
, UErrorCode
& /*status*/) {
763 if(index
< availableLocaleListCount
) {
764 result
= availableLocaleList
[index
++].getName();
765 if(resultLength
!= NULL
) {
766 *resultLength
= (int32_t)uprv_strlen(result
);
769 if(resultLength
!= NULL
) {
777 virtual const UnicodeString
* snext(UErrorCode
& status
) {
778 int32_t resultLength
= 0;
779 const char *s
= next(&resultLength
, status
);
780 return setChars(s
, resultLength
, status
);
783 virtual void reset(UErrorCode
& /*status*/) {
788 CollationLocaleListEnumeration::~CollationLocaleListEnumeration() {}
790 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationLocaleListEnumeration
)
793 // -------------------------------------
795 StringEnumeration
* U_EXPORT2
796 Collator::getAvailableLocales(void)
798 #if !UCONFIG_NO_SERVICE
800 return getService()->getAvailableLocales();
802 #endif /* UCONFIG_NO_SERVICE */
803 UErrorCode status
= U_ZERO_ERROR
;
804 if (isAvailableLocaleListInitialized(status
)) {
805 return new CollationLocaleListEnumeration();
810 StringEnumeration
* U_EXPORT2
811 Collator::getKeywords(UErrorCode
& status
) {
812 // This is a wrapper over ucol_getKeywords
813 UEnumeration
* uenum
= ucol_getKeywords(&status
);
814 if (U_FAILURE(status
)) {
818 return new UStringEnumeration(uenum
);
821 StringEnumeration
* U_EXPORT2
822 Collator::getKeywordValues(const char *keyword
, UErrorCode
& status
) {
823 // This is a wrapper over ucol_getKeywordValues
824 UEnumeration
* uenum
= ucol_getKeywordValues(keyword
, &status
);
825 if (U_FAILURE(status
)) {
829 return new UStringEnumeration(uenum
);
832 StringEnumeration
* U_EXPORT2
833 Collator::getKeywordValuesForLocale(const char* key
, const Locale
& locale
,
834 UBool commonlyUsed
, UErrorCode
& status
) {
835 // This is a wrapper over ucol_getKeywordValuesForLocale
836 UEnumeration
*uenum
= ucol_getKeywordValuesForLocale(key
, locale
.getName(),
837 commonlyUsed
, &status
);
838 if (U_FAILURE(status
)) {
842 return new UStringEnumeration(uenum
);
846 Collator::getFunctionalEquivalent(const char* keyword
, const Locale
& locale
,
847 UBool
& isAvailable
, UErrorCode
& status
) {
848 // This is a wrapper over ucol_getFunctionalEquivalent
849 char loc
[ULOC_FULLNAME_CAPACITY
];
850 /*int32_t len =*/ ucol_getFunctionalEquivalent(loc
, sizeof(loc
),
851 keyword
, locale
.getName(), &isAvailable
, &status
);
852 if (U_FAILURE(status
)) {
855 return Locale::createFromName(loc
);
859 Collator::getReorderCodes(int32_t* /* dest*/,
860 int32_t /* destCapacity*/,
861 UErrorCode
& status
) const
863 if (U_SUCCESS(status
)) {
864 status
= U_UNSUPPORTED_ERROR
;
870 Collator::setReorderCodes(const int32_t* /* reorderCodes */,
871 int32_t /* reorderCodesLength */,
874 if (U_SUCCESS(status
)) {
875 status
= U_UNSUPPORTED_ERROR
;
880 Collator::getEquivalentReorderCodes(int32_t /* reorderCode */,
882 int32_t /* destCapacity */,
885 if (U_SUCCESS(status
)) {
886 status
= U_UNSUPPORTED_ERROR
;
892 Collator::internalGetShortDefinitionString(const char * /*locale*/,
894 int32_t /*capacity*/,
895 UErrorCode
&status
) const {
896 if(U_SUCCESS(status
)) {
897 status
= U_UNSUPPORTED_ERROR
; /* Shouldn't happen, internal function */
902 // UCollator private data members ----------------------------------------
904 /* This is useless information */
905 /*const UVersionInfo Collator::fVersion = {1, 1, 0, 0};*/
907 // -------------------------------------
911 #endif /* #if !UCONFIG_NO_COLLATION */