michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCollationMacUC.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "prmem.h" michael@0: #include "nsString.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCollationMacUC, nsICollation) michael@0: michael@0: nsCollationMacUC::nsCollationMacUC() michael@0: : mInit(false) michael@0: , mHasCollator(false) michael@0: , mLocale(nullptr) michael@0: , mLastStrength(-1) michael@0: , mCollator(nullptr) michael@0: , mBuffer(nullptr) michael@0: , mBufferLen(1) michael@0: { michael@0: } michael@0: michael@0: nsCollationMacUC::~nsCollationMacUC() michael@0: { michael@0: if (mHasCollator) { michael@0: #ifdef DEBUG michael@0: OSStatus err = michael@0: #endif michael@0: ::UCDisposeCollator(&mCollator); michael@0: mHasCollator = false; michael@0: NS_ASSERTION((err == noErr), "UCDisposeCollator failed"); michael@0: } michael@0: PR_FREEIF(mBuffer); michael@0: } michael@0: michael@0: nsresult nsCollationMacUC::StrengthToOptions(const int32_t aStrength, michael@0: UCCollateOptions* aOptions) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOptions); michael@0: NS_ENSURE_TRUE((aStrength < 4), NS_ERROR_FAILURE); michael@0: // set our default collation options michael@0: UCCollateOptions options = kUCCollateStandardOptions | kUCCollatePunctuationSignificantMask; michael@0: if (aStrength & kCollationCaseInsensitiveAscii) michael@0: options |= kUCCollateCaseInsensitiveMask; michael@0: if (aStrength & kCollationAccentInsenstive) michael@0: options |= kUCCollateDiacritInsensitiveMask; michael@0: *aOptions = options; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsCollationMacUC::ConvertLocale(nsILocale* aNSLocale, LocaleRef* aMacLocale) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aNSLocale); michael@0: NS_ENSURE_ARG_POINTER(aMacLocale); michael@0: michael@0: nsAutoString localeString; michael@0: nsresult res = aNSLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), localeString); michael@0: NS_ENSURE_TRUE(NS_SUCCEEDED(res) && !localeString.IsEmpty(), michael@0: NS_ERROR_FAILURE); michael@0: NS_LossyConvertUTF16toASCII tmp(localeString); michael@0: tmp.ReplaceChar('-', '_'); michael@0: OSStatus err; michael@0: err = ::LocaleRefFromLocaleString(tmp.get(), aMacLocale); michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsCollationMacUC::EnsureCollator(const int32_t newStrength) michael@0: { michael@0: NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); michael@0: if (mHasCollator && (mLastStrength == newStrength)) michael@0: return NS_OK; michael@0: michael@0: OSStatus err; michael@0: if (mHasCollator) { michael@0: err = ::UCDisposeCollator(&mCollator); michael@0: mHasCollator = false; michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: UCCollateOptions newOptions; michael@0: nsresult res = StrengthToOptions(newStrength, &newOptions); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: LocaleOperationVariant opVariant = 0; // default variant for now michael@0: err = ::UCCreateCollator(mLocale, opVariant, newOptions, &mCollator); michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: mHasCollator = true; michael@0: michael@0: mLastStrength = newStrength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCollationMacUC::Initialize(nsILocale* locale) michael@0: { michael@0: NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED); michael@0: nsCOMPtr appLocale; michael@0: michael@0: nsresult rv; michael@0: if (!locale) { michael@0: nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: locale = appLocale; michael@0: } michael@0: michael@0: rv = ConvertLocale(locale, &mLocale); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mInit = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCollationMacUC::AllocateRawSortKey(int32_t strength, const nsAString& stringIn, michael@0: uint8_t** key, uint32_t* outLen) michael@0: { michael@0: NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_ARG_POINTER(key); michael@0: NS_ENSURE_ARG_POINTER(outLen); michael@0: michael@0: nsresult res = EnsureCollator(strength); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: uint32_t stringInLen = stringIn.Length(); michael@0: uint32_t maxKeyLen = (1 + stringInLen) * kCollationValueSizeFactor * sizeof(UCCollationValue); michael@0: if (maxKeyLen > mBufferLen) { michael@0: uint32_t newBufferLen = mBufferLen; michael@0: do { michael@0: newBufferLen *= 2; michael@0: } while (newBufferLen < maxKeyLen); michael@0: void *newBuffer = PR_Malloc(newBufferLen); michael@0: if (!newBuffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: PR_FREEIF(mBuffer); michael@0: mBuffer = newBuffer; michael@0: mBufferLen = newBufferLen; michael@0: } michael@0: michael@0: ItemCount actual; michael@0: OSStatus err = ::UCGetCollationKey(mCollator, (const UniChar*) PromiseFlatString(stringIn).get(), michael@0: (UniCharCount) stringInLen, michael@0: (ItemCount) (mBufferLen / sizeof(UCCollationValue)), michael@0: &actual, (UCCollationValue *)mBuffer); michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: michael@0: uint32_t keyLength = actual * sizeof(UCCollationValue); michael@0: void *newKey = PR_Malloc(keyLength); michael@0: if (!newKey) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: memcpy(newKey, mBuffer, keyLength); michael@0: *key = (uint8_t *)newKey; michael@0: *outLen = keyLength; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCollationMacUC::CompareString(int32_t strength, const nsAString& string1, michael@0: const nsAString& string2, int32_t* result) michael@0: { michael@0: NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_ARG_POINTER(result); michael@0: *result = 0; michael@0: michael@0: nsresult res = EnsureCollator(strength); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: *result = 0; michael@0: michael@0: OSStatus err; michael@0: err = ::UCCompareText(mCollator, michael@0: (const UniChar *) PromiseFlatString(string1).get(), (UniCharCount) string1.Length(), michael@0: (const UniChar *) PromiseFlatString(string2).get(), (UniCharCount) string2.Length(), michael@0: nullptr, (SInt32*) result); michael@0: michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCollationMacUC::CompareRawSortKey(const uint8_t* key1, uint32_t len1, michael@0: const uint8_t* key2, uint32_t len2, michael@0: int32_t* result) michael@0: { michael@0: NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_ARG_POINTER(key1); michael@0: NS_ENSURE_ARG_POINTER(key2); michael@0: NS_ENSURE_ARG_POINTER(result); michael@0: *result = 0; michael@0: michael@0: OSStatus err; michael@0: err = ::UCCompareCollationKeys((const UCCollationValue*) key1, (ItemCount) len1, michael@0: (const UCCollationValue*) key2, (ItemCount) len2, michael@0: nullptr, (SInt32*) result); michael@0: michael@0: NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: }