diff options
Diffstat (limited to '')
-rw-r--r-- | intl/locale/nsCollation.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/intl/locale/nsCollation.cpp b/intl/locale/nsCollation.cpp new file mode 100644 index 0000000000..96f0198980 --- /dev/null +++ b/intl/locale/nsCollation.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCollation.h" +#include "mozilla/intl/LocaleService.h" +#include "nsString.h" + +NS_IMPL_ISUPPORTS(nsCollation, nsICollation) + +nsCollation::nsCollation() + : mInit(false), + mHasCollator(false), + mLastStrength(-1), + mCollatorICU(nullptr) {} + +nsCollation::~nsCollation() { +#ifdef DEBUG + nsresult res = +#endif + CleanUpCollator(); + NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed"); +} + +nsresult nsCollation::ConvertStrength(const int32_t aNSStrength, + UCollationStrength* aICUStrength, + UColAttributeValue* aCaseLevelOut) { + NS_ENSURE_ARG_POINTER(aICUStrength); + NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE); + + UCollationStrength strength = UCOL_DEFAULT; + UColAttributeValue caseLevel = UCOL_OFF; + switch (aNSStrength) { + case kCollationCaseInSensitive: + strength = UCOL_PRIMARY; + break; + case kCollationCaseInsensitiveAscii: + strength = UCOL_SECONDARY; + break; + case kCollationAccentInsenstive: + caseLevel = UCOL_ON; + strength = UCOL_PRIMARY; + break; + case kCollationCaseSensitive: + strength = UCOL_TERTIARY; + break; + default: + NS_WARNING("Bad aNSStrength passed to ConvertStrength."); + return NS_ERROR_FAILURE; + } + + *aICUStrength = strength; + *aCaseLevelOut = caseLevel; + + return NS_OK; +} + +nsresult nsCollation::EnsureCollator(const int32_t newStrength) { + NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); + if (mHasCollator && (mLastStrength == newStrength)) return NS_OK; + + nsresult res; + res = CleanUpCollator(); + NS_ENSURE_SUCCESS(res, res); + + UErrorCode status; + status = U_ZERO_ERROR; + mCollatorICU = ucol_open(mLocale.get(), &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + + UCollationStrength strength; + UColAttributeValue caseLevel; + res = ConvertStrength(newStrength, &strength, &caseLevel); + NS_ENSURE_SUCCESS(res, res); + + status = U_ZERO_ERROR; + ucol_setAttribute(mCollatorICU, UCOL_STRENGTH, strength, &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + ucol_setAttribute(mCollatorICU, UCOL_CASE_LEVEL, caseLevel, &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + ucol_setAttribute(mCollatorICU, UCOL_ALTERNATE_HANDLING, UCOL_DEFAULT, + &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + ucol_setAttribute(mCollatorICU, UCOL_NUMERIC_COLLATION, UCOL_OFF, &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + ucol_setAttribute(mCollatorICU, UCOL_NORMALIZATION_MODE, UCOL_ON, &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + ucol_setAttribute(mCollatorICU, UCOL_CASE_FIRST, UCOL_DEFAULT, &status); + NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE); + + mHasCollator = true; + + mLastStrength = newStrength; + return NS_OK; +} + +nsresult nsCollation::CleanUpCollator(void) { + if (mHasCollator) { + ucol_close(mCollatorICU); + mHasCollator = false; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCollation::Initialize(const nsACString& locale) { + NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED); + + // Check whether locale parameter is valid. If no, use application locale + UErrorCode status = U_ZERO_ERROR; + UCollator* collator = ucol_open(PromiseFlatCString(locale).get(), &status); + if (U_SUCCESS(status)) { + mLocale = locale; + } else { + status = U_ZERO_ERROR; + mozilla::LocaleService::GetInstance()->GetAppLocaleAsBCP47(mLocale); + collator = ucol_open(mLocale.get(), &status); + if (NS_WARN_IF(U_FAILURE(status))) { + return NS_ERROR_UNEXPECTED; + } + } + ucol_close(collator); + + mInit = true; + return NS_OK; +} + +NS_IMETHODIMP +nsCollation::AllocateRawSortKey(int32_t strength, const nsAString& stringIn, + nsTArray<uint8_t>& key) { + NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); + + nsresult res = EnsureCollator(strength); + NS_ENSURE_SUCCESS(res, res); + + uint32_t stringInLen = stringIn.Length(); + + const UChar* str = (const UChar*)stringIn.BeginReading(); + + int32_t keyLength = + ucol_getSortKey(mCollatorICU, str, stringInLen, nullptr, 0); + NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE); + + key.SetLength(keyLength + 1); + + keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, key.Elements(), + keyLength + 1); + NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE); + + key.SetLength(keyLength); + return NS_OK; +} + +NS_IMETHODIMP +nsCollation::CompareString(int32_t strength, const nsAString& string1, + const nsAString& string2, int32_t* result) { + NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_ARG_POINTER(result); + *result = 0; + + nsresult rv = EnsureCollator(strength); + NS_ENSURE_SUCCESS(rv, rv); + + UCollationResult uresult; + uresult = ucol_strcoll(mCollatorICU, (const UChar*)string1.BeginReading(), + string1.Length(), (const UChar*)string2.BeginReading(), + string2.Length()); + int32_t res; + switch (uresult) { + case UCOL_LESS: + res = -1; + break; + case UCOL_EQUAL: + res = 0; + break; + case UCOL_GREATER: + res = 1; + break; + default: + MOZ_CRASH("ucol_strcoll returned bad UCollationResult"); + } + *result = res; + return NS_OK; +} + +NS_IMETHODIMP +nsCollation::CompareRawSortKey(const nsTArray<uint8_t>& key1, + const nsTArray<uint8_t>& key2, int32_t* result) { + NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_ARG_POINTER(result); + *result = 0; + + size_t minLength = std::min(key1.Length(), key2.Length()); + int32_t tmpResult = strncmp((const char*)key1.Elements(), + (const char*)key2.Elements(), minLength); + int32_t res; + if (tmpResult < 0) { + res = -1; + } else if (tmpResult > 0) { + res = 1; + } else if (key1.Length() > minLength) { + // First string contains second one, so comes later, hence return > 0. + res = 1; + } else if (key2.Length() > minLength) { + // First string is a substring of second one, so comes earlier, + // hence return < 0. + res = -1; + } else { + res = 0; + } + *result = res; + return NS_OK; +} |