2 ******************************************************************************
3 * Copyright (C) 1996-2010, 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 U_NAMESPACE_QUALIFIER Locale
* availableLocaleList
= NULL
;
56 static int32_t availableLocaleListCount
;
57 static U_NAMESPACE_QUALIFIER 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
)) { }
117 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const;
121 ICUCollatorFactory::create(const ICUServiceKey
& key
, const ICUService
* /* service */, UErrorCode
& status
) const {
122 if (handlesKey(key
, status
)) {
123 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
125 // make sure the requested locale is correct
126 // default LocaleFactory uses currentLocale since that's the one vetted by handlesKey
127 // but for ICU rb resources we use the actual one since it will fallback again
128 lkey
.canonicalLocale(loc
);
130 return Collator::makeInstance(loc
, status
);
135 // -------------------------------------
137 class ICUCollatorService
: public ICULocaleService
{
140 : ICULocaleService(UNICODE_STRING_SIMPLE("Collator"))
142 UErrorCode status
= U_ZERO_ERROR
;
143 registerFactory(new ICUCollatorFactory(), status
);
146 virtual UObject
* cloneInstance(UObject
* instance
) const {
147 return ((Collator
*)instance
)->clone();
150 virtual UObject
* handleDefault(const ICUServiceKey
& key
, UnicodeString
* actualID
, UErrorCode
& status
) const {
151 LocaleKey
& lkey
= (LocaleKey
&)key
;
153 // Ugly Hack Alert! We return an empty actualID to signal
154 // to callers that this is a default object, not a "real"
155 // service-created object. (TODO remove in 3.0) [aliu]
156 actualID
->truncate(0);
159 lkey
.canonicalLocale(loc
);
160 return Collator::makeInstance(loc
, status
);
163 virtual UObject
* getKey(ICUServiceKey
& key
, UnicodeString
* actualReturn
, UErrorCode
& status
) const {
165 if (actualReturn
== NULL
) {
168 Collator
* result
= (Collator
*)ICULocaleService::getKey(key
, actualReturn
, status
);
169 // Ugly Hack Alert! If the actualReturn length is zero, this
170 // means we got a default object, not a "real" service-created
171 // object. We don't call setLocales() on a default object,
172 // because that will overwrite its correct built-in locale
173 // metadata (valid & actual) with our incorrect data (all we
174 // have is the requested locale). (TODO remove in 3.0) [aliu]
175 if (result
&& actualReturn
->length() > 0) {
176 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
177 Locale
canonicalLocale("");
178 Locale
currentLocale("");
180 LocaleUtility::initLocaleFromName(*actualReturn
, currentLocale
);
181 result
->setLocales(lkey
.canonicalLocale(canonicalLocale
), currentLocale
, currentLocale
);
186 virtual UBool
isDefault() const {
187 return countFactories() == 1;
191 // -------------------------------------
193 static ICULocaleService
*
197 UMTX_CHECK(NULL
, (UBool
)(gService
== NULL
), needInit
);
199 ICULocaleService
*newservice
= new ICUCollatorService();
202 if(gService
== NULL
) {
203 gService
= newservice
;
212 ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR
, collator_cleanup
);
218 // -------------------------------------
224 UMTX_CHECK(NULL
, gService
!= NULL
, retVal
);
228 // -------------------------------------
231 Collator::createUCollator(const char *loc
,
234 UCollator
*result
= 0;
235 if (status
&& U_SUCCESS(*status
) && hasService()) {
236 Locale
desiredLocale(loc
);
237 Collator
*col
= (Collator
*)gService
->get(desiredLocale
, *status
);
238 RuleBasedCollator
*rbc
;
239 if (col
&& (rbc
= dynamic_cast<RuleBasedCollator
*>(col
))) {
240 if (!rbc
->dataIsOwned
) {
241 result
= ucol_safeClone(rbc
->ucollator
, NULL
, NULL
, status
);
243 result
= rbc
->ucollator
;
244 rbc
->ucollator
= NULL
; // to prevent free on delete
251 #endif /* UCONFIG_NO_SERVICE */
253 static UBool
isAvailableLocaleListInitialized(UErrorCode
&status
) {
254 // for now, there is a hardcoded list, so just walk through that list and set it up.
256 UMTX_CHECK(NULL
, availableLocaleList
== NULL
, needInit
);
259 UResourceBundle
*index
= NULL
;
260 UResourceBundle installed
;
265 ures_initStackObject(&installed
);
266 index
= ures_openDirect(U_ICUDATA_COLL
, "res_index", &status
);
267 ures_getByKey(index
, "InstalledLocales", &installed
, &status
);
269 if(U_SUCCESS(status
)) {
270 localeCount
= ures_getSize(&installed
);
271 temp
= new Locale
[localeCount
];
274 ures_resetIterator(&installed
);
275 while(ures_hasNext(&installed
)) {
276 const char *tempKey
= NULL
;
277 ures_getNextString(&installed
, NULL
, &tempKey
, &status
);
278 temp
[i
++] = Locale(tempKey
);
282 if (availableLocaleList
== NULL
)
284 availableLocaleListCount
= localeCount
;
285 availableLocaleList
= temp
;
287 ucln_i18n_registerCleanup(UCLN_I18N_COLLATOR
, collator_cleanup
);
297 ures_close(&installed
);
304 // Collator public methods -----------------------------------------------
306 Collator
* U_EXPORT2
Collator::createInstance(UErrorCode
& success
)
308 return createInstance(Locale::getDefault(), success
);
311 Collator
* U_EXPORT2
Collator::createInstance(const Locale
& desiredLocale
,
314 if (U_FAILURE(status
))
317 #if !UCONFIG_NO_SERVICE
321 (Collator
*)gService
->get(desiredLocale
, &actualLoc
, status
);
322 // Ugly Hack Alert! If the returned locale is empty (not root,
323 // but empty -- getName() == "") then that means the service
324 // returned a default object, not a "real" service object. In
325 // that case, the locale metadata (valid & actual) is setup
326 // correctly already, and we don't want to overwrite it. (TODO
327 // remove in 3.0) [aliu]
328 if (*actualLoc
.getName() != 0) {
329 result
->setLocales(desiredLocale
, actualLoc
, actualLoc
);
334 return makeInstance(desiredLocale
, status
);
338 Collator
* Collator::makeInstance(const Locale
& desiredLocale
,
341 // A bit of explanation is required here. Although in the current
343 // Collator::createInstance() is just turning around and calling
344 // RuleBasedCollator(Locale&), this will not necessarily always be the
345 // case. For example, suppose we modify this code to handle a
346 // non-table-based Collator, such as that for Thai. In this case,
347 // createInstance() will have to be modified to somehow determine this fact
348 // (perhaps a field in the resource bundle). Then it can construct the
349 // non-table-based Collator in some other way, when it sees that it needs
351 // The specific caution is this: RuleBasedCollator(Locale&) will ALWAYS
352 // return a valid collation object, if the system is functioning properly.
353 // The reason is that it will fall back, use the default locale, and even
354 // use the built-in default collation rules. THEREFORE, createInstance()
355 // should in general ONLY CALL RuleBasedCollator(Locale&) IF IT KNOWS IN
356 // ADVANCE that the given locale's collation is properly implemented as a
357 // RuleBasedCollator.
358 // Currently, we don't do this...we always return a RuleBasedCollator,
359 // whether it is strictly correct to do so or not, without checking, because
360 // we currently have no way of checking.
362 RuleBasedCollator
* collation
= new RuleBasedCollator(desiredLocale
,
365 if (collation
== 0) {
366 status
= U_MEMORY_ALLOCATION_ERROR
;
369 if (U_FAILURE(status
))
377 #ifdef U_USE_COLLATION_OBSOLETE_2_6
378 // !!! dlf the following is obsolete, ignore registration for this
381 Collator::createInstance(const Locale
&loc
,
382 UVersionInfo version
,
388 collator
=new RuleBasedCollator(loc
, status
);
391 status
= U_MEMORY_ALLOCATION_ERROR
;
395 if(U_SUCCESS(status
)) {
396 collator
->getVersion(info
);
397 if(0!=uprv_memcmp(version
, info
, sizeof(UVersionInfo
))) {
399 status
=U_MISSING_RESOURCE_ERROR
;
407 // implement deprecated, previously abstract method
408 Collator::EComparisonResult
Collator::compare(const UnicodeString
& source
,
409 const UnicodeString
& target
) const
411 UErrorCode ec
= U_ZERO_ERROR
;
412 return (Collator::EComparisonResult
)compare(source
, target
, ec
);
415 // implement deprecated, previously abstract method
416 Collator::EComparisonResult
Collator::compare(const UnicodeString
& source
,
417 const UnicodeString
& target
,
418 int32_t length
) const
420 UErrorCode ec
= U_ZERO_ERROR
;
421 return (Collator::EComparisonResult
)compare(source
, target
, length
, ec
);
424 // implement deprecated, previously abstract method
425 Collator::EComparisonResult
Collator::compare(const UChar
* source
, int32_t sourceLength
,
426 const UChar
* target
, int32_t targetLength
)
429 UErrorCode ec
= U_ZERO_ERROR
;
430 return (Collator::EComparisonResult
)compare(source
, sourceLength
, target
, targetLength
, ec
);
433 UCollationResult
Collator::compare(UCharIterator
&/*sIter*/,
434 UCharIterator
&/*tIter*/,
435 UErrorCode
&status
) const {
436 if(U_SUCCESS(status
)) {
437 // Not implemented in the base class.
438 status
= U_UNSUPPORTED_ERROR
;
443 UCollationResult
Collator::compareUTF8(const StringPiece
&source
,
444 const StringPiece
&target
,
445 UErrorCode
&status
) const {
446 if(U_FAILURE(status
)) {
449 UCharIterator sIter
, tIter
;
450 uiter_setUTF8(&sIter
, source
.data(), source
.length());
451 uiter_setUTF8(&tIter
, target
.data(), target
.length());
452 return compare(sIter
, tIter
, status
);
455 UBool
Collator::equals(const UnicodeString
& source
,
456 const UnicodeString
& target
) const
458 UErrorCode ec
= U_ZERO_ERROR
;
459 return (compare(source
, target
, ec
) == UCOL_EQUAL
);
462 UBool
Collator::greaterOrEqual(const UnicodeString
& source
,
463 const UnicodeString
& target
) const
465 UErrorCode ec
= U_ZERO_ERROR
;
466 return (compare(source
, target
, ec
) != UCOL_LESS
);
469 UBool
Collator::greater(const UnicodeString
& source
,
470 const UnicodeString
& target
) const
472 UErrorCode ec
= U_ZERO_ERROR
;
473 return (compare(source
, target
, ec
) == UCOL_GREATER
);
476 // this API ignores registered collators, since it returns an
477 // array of indefinite lifetime
478 const Locale
* U_EXPORT2
Collator::getAvailableLocales(int32_t& count
)
480 UErrorCode status
= U_ZERO_ERROR
;
481 Locale
*result
= NULL
;
483 if (isAvailableLocaleListInitialized(status
))
485 result
= availableLocaleList
;
486 count
= availableLocaleListCount
;
491 UnicodeString
& U_EXPORT2
Collator::getDisplayName(const Locale
& objectLocale
,
492 const Locale
& displayLocale
,
495 #if !UCONFIG_NO_SERVICE
497 UnicodeString locNameStr
;
498 LocaleUtility::initNameFromLocale(objectLocale
, locNameStr
);
499 return gService
->getDisplayName(locNameStr
, name
, displayLocale
);
502 return objectLocale
.getDisplayName(displayLocale
, name
);
505 UnicodeString
& U_EXPORT2
Collator::getDisplayName(const Locale
& objectLocale
,
508 return getDisplayName(objectLocale
, Locale::getDefault(), name
);
511 /* This is useless information */
512 /*void Collator::getVersion(UVersionInfo versionInfo) const
514 if (versionInfo!=NULL)
515 uprv_memcpy(versionInfo, fVersion, U_MAX_VERSION_LENGTH);
519 // UCollator protected constructor destructor ----------------------------
522 * Default constructor.
523 * Constructor is different from the old default Collator constructor.
524 * The task for determing the default collation strength and normalization mode
525 * is left to the child class.
534 * Empty constructor, does not handle the arguments.
535 * This constructor is done for backward compatibility with 1.7 and 1.8.
536 * The task for handling the argument collation strength and normalization
537 * mode is left to the child class.
538 * @param collationStrength collation strength
539 * @param decompositionMode
540 * @deprecated 2.4 use the default constructor instead
542 Collator::Collator(UCollationStrength
, UNormalizationMode
)
547 Collator::~Collator()
551 Collator::Collator(const Collator
&other
)
556 UBool
Collator::operator==(const Collator
& other
) const
558 return (UBool
)(this == &other
);
561 UBool
Collator::operator!=(const Collator
& other
) const
563 return (UBool
)!(*this == other
);
566 int32_t U_EXPORT2
Collator::getBound(const uint8_t *source
,
567 int32_t sourceLength
,
568 UColBoundMode boundType
,
571 int32_t resultLength
,
574 return ucol_getBound(source
, sourceLength
, boundType
, noOfLevels
, result
, resultLength
, &status
);
578 Collator::setLocales(const Locale
& /* requestedLocale */, const Locale
& /* validLocale */, const Locale
& /*actualLocale*/) {
581 UnicodeSet
*Collator::getTailoredSet(UErrorCode
&status
) const
583 if(U_FAILURE(status
)) {
586 // everything can be changed
587 return new UnicodeSet(0, 0x10FFFF);
590 // -------------------------------------
592 #if !UCONFIG_NO_SERVICE
593 URegistryKey U_EXPORT2
594 Collator::registerInstance(Collator
* toAdopt
, const Locale
& locale
, UErrorCode
& status
)
596 if (U_SUCCESS(status
)) {
597 return getService()->registerInstance(toAdopt
, locale
, status
);
602 // -------------------------------------
604 class CFactory
: public LocaleKeyFactory
{
606 CollatorFactory
* _delegate
;
610 CFactory(CollatorFactory
* delegate
, UErrorCode
& status
)
611 : LocaleKeyFactory(delegate
->visible() ? VISIBLE
: INVISIBLE
)
612 , _delegate(delegate
)
615 if (U_SUCCESS(status
)) {
617 _ids
= new Hashtable(status
);
619 const UnicodeString
* idlist
= _delegate
->getSupportedIDs(count
, status
);
620 for (int i
= 0; i
< count
; ++i
) {
621 _ids
->put(idlist
[i
], (void*)this, status
);
622 if (U_FAILURE(status
)) {
629 status
= U_MEMORY_ALLOCATION_ERROR
;
640 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* service
, UErrorCode
& status
) const;
643 virtual const Hashtable
* getSupportedIDs(UErrorCode
& status
) const
645 if (U_SUCCESS(status
)) {
651 virtual UnicodeString
&
652 getDisplayName(const UnicodeString
& id
, const Locale
& locale
, UnicodeString
& result
) const;
656 CFactory::create(const ICUServiceKey
& key
, const ICUService
* /* service */, UErrorCode
& status
) const
658 if (handlesKey(key
, status
)) {
659 const LocaleKey
& lkey
= (const LocaleKey
&)key
;
661 lkey
.currentLocale(validLoc
);
662 return _delegate
->createCollator(validLoc
);
668 CFactory::getDisplayName(const UnicodeString
& id
, const Locale
& locale
, UnicodeString
& result
) const
670 if ((_coverage
& 0x1) == 0) {
671 UErrorCode status
= U_ZERO_ERROR
;
672 const Hashtable
* ids
= getSupportedIDs(status
);
673 if (ids
&& (ids
->get(id
) != NULL
)) {
675 LocaleUtility::initLocaleFromName(id
, loc
);
676 return _delegate
->getDisplayName(loc
, locale
, result
);
683 URegistryKey U_EXPORT2
684 Collator::registerFactory(CollatorFactory
* toAdopt
, UErrorCode
& status
)
686 if (U_SUCCESS(status
)) {
687 CFactory
* f
= new CFactory(toAdopt
, status
);
689 return getService()->registerFactory(f
, status
);
691 status
= U_MEMORY_ALLOCATION_ERROR
;
696 // -------------------------------------
699 Collator::unregister(URegistryKey key
, UErrorCode
& status
)
701 if (U_SUCCESS(status
)) {
703 return gService
->unregister(key
, status
);
705 status
= U_ILLEGAL_ARGUMENT_ERROR
;
709 #endif /* UCONFIG_NO_SERVICE */
711 class CollationLocaleListEnumeration
: public StringEnumeration
{
715 static UClassID U_EXPORT2
getStaticClassID(void);
716 virtual UClassID
getDynamicClassID(void) const;
718 CollationLocaleListEnumeration()
721 // The global variables should already be initialized.
722 //isAvailableLocaleListInitialized(status);
725 virtual ~CollationLocaleListEnumeration() {
728 virtual StringEnumeration
* clone() const
730 CollationLocaleListEnumeration
*result
= new CollationLocaleListEnumeration();
732 result
->index
= index
;
737 virtual int32_t count(UErrorCode
&/*status*/) const {
738 return availableLocaleListCount
;
741 virtual const char* next(int32_t* resultLength
, UErrorCode
& /*status*/) {
743 if(index
< availableLocaleListCount
) {
744 result
= availableLocaleList
[index
++].getName();
745 if(resultLength
!= NULL
) {
746 *resultLength
= (int32_t)uprv_strlen(result
);
749 if(resultLength
!= NULL
) {
757 virtual const UnicodeString
* snext(UErrorCode
& status
) {
758 int32_t resultLength
= 0;
759 const char *s
= next(&resultLength
, status
);
760 return setChars(s
, resultLength
, status
);
763 virtual void reset(UErrorCode
& /*status*/) {
768 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CollationLocaleListEnumeration
)
771 // -------------------------------------
773 StringEnumeration
* U_EXPORT2
774 Collator::getAvailableLocales(void)
776 #if !UCONFIG_NO_SERVICE
778 return getService()->getAvailableLocales();
780 #endif /* UCONFIG_NO_SERVICE */
781 UErrorCode status
= U_ZERO_ERROR
;
782 if (isAvailableLocaleListInitialized(status
)) {
783 return new CollationLocaleListEnumeration();
788 StringEnumeration
* U_EXPORT2
789 Collator::getKeywords(UErrorCode
& status
) {
790 // This is a wrapper over ucol_getKeywords
791 UEnumeration
* uenum
= ucol_getKeywords(&status
);
792 if (U_FAILURE(status
)) {
796 return new UStringEnumeration(uenum
);
799 StringEnumeration
* U_EXPORT2
800 Collator::getKeywordValues(const char *keyword
, UErrorCode
& status
) {
801 // This is a wrapper over ucol_getKeywordValues
802 UEnumeration
* uenum
= ucol_getKeywordValues(keyword
, &status
);
803 if (U_FAILURE(status
)) {
807 return new UStringEnumeration(uenum
);
810 StringEnumeration
* U_EXPORT2
811 Collator::getKeywordValuesForLocale(const char* key
, const Locale
& locale
,
812 UBool commonlyUsed
, UErrorCode
& status
) {
813 // This is a wrapper over ucol_getKeywordValuesForLocale
814 UEnumeration
*uenum
= ucol_getKeywordValuesForLocale(key
, locale
.getName(),
815 commonlyUsed
, &status
);
816 if (U_FAILURE(status
)) {
820 return new UStringEnumeration(uenum
);
824 Collator::getFunctionalEquivalent(const char* keyword
, const Locale
& locale
,
825 UBool
& isAvailable
, UErrorCode
& status
) {
826 // This is a wrapper over ucol_getFunctionalEquivalent
827 char loc
[ULOC_FULLNAME_CAPACITY
];
828 /*int32_t len =*/ ucol_getFunctionalEquivalent(loc
, sizeof(loc
),
829 keyword
, locale
.getName(), &isAvailable
, &status
);
830 if (U_FAILURE(status
)) {
833 return Locale::createFromName(loc
);
836 int32_t Collator::getReorderCodes(int32_t *dest
,
837 int32_t destCapacity
,
838 UErrorCode
& status
) const
840 if (U_SUCCESS(status
)) {
841 status
= U_UNSUPPORTED_ERROR
;
846 void Collator::setReorderCodes(const int32_t *reorderCodes
,
847 int32_t reorderCodesLength
,
850 if (U_SUCCESS(status
)) {
851 status
= U_UNSUPPORTED_ERROR
;
855 // UCollator private data members ----------------------------------------
857 /* This is useless information */
858 /*const UVersionInfo Collator::fVersion = {1, 1, 0, 0};*/
860 // -------------------------------------
864 #endif /* #if !UCONFIG_NO_COLLATION */