| /* |
| * Copyright 1999-2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "ICUBridgeCollationCompareFunctorImpl.hpp" |
| #include "ICUBridge.hpp" |
| |
| |
| |
| #include <algorithm> |
| #include <cstdlib> |
| |
| |
| |
| #include <xalanc/Include/XalanAutoPtr.hpp> |
| |
| |
| |
| #include <xalanc/PlatformSupport/DOMStringHelper.hpp> |
| #include <xalanc/PlatformSupport/XalanUnicode.hpp> |
| |
| |
| |
| XALAN_CPP_NAMESPACE_BEGIN |
| |
| |
| |
| const StylesheetExecutionContextDefault::DefaultCollationCompareFunctor ICUBridgeCollationCompareFunctorImpl::s_defaultFunctor; |
| |
| |
| |
| inline ICUBridgeCollationCompareFunctorImpl::CollatorType* |
| createCollator( |
| UErrorCode& theStatus, |
| const Locale& theLocale, |
| XalanDOMString* theLocaleName = 0) |
| { |
| typedef ICUBridgeCollationCompareFunctorImpl::CollatorType CollatorType; |
| |
| if (theLocaleName != 0) |
| { |
| *theLocaleName = theLocale.getName(); |
| |
| // Replace _ with -, since that's what xml:lang specifies... |
| XalanDOMString::size_type theIndex; |
| |
| while((theIndex = indexOf(*theLocaleName, XalanUnicode::charLowLine)) != theLocaleName->length()) |
| { |
| (*theLocaleName)[theIndex] = XalanUnicode::charHyphenMinus; |
| } |
| } |
| |
| return CollatorType::createInstance(theLocale, theStatus); |
| } |
| |
| |
| |
| inline ICUBridgeCollationCompareFunctorImpl::CollatorType* |
| createCollator( |
| UErrorCode& theStatus, |
| XalanDOMString* theLocaleName = 0) |
| { |
| const char* theLang = |
| #if defined(XALAN_STRICT_ANSI_HEADERS) |
| std::getenv("LANG"); |
| #else |
| getenv("LANG"); |
| #endif |
| |
| if (theLang == 0) |
| { |
| #if defined(XALAN_ICU_DEFAULT_LOCALE_PROBLEM) |
| return createCollator(theStatus, Locale::US, theLocaleName); |
| #else |
| return createCollator(theStatus, Locale::getDefault(), theLocaleName); |
| #endif |
| } |
| else |
| { |
| return createCollator(theStatus, Locale(theLang), theLocaleName); |
| } |
| } |
| |
| |
| |
| ICUBridgeCollationCompareFunctorImpl::ICUBridgeCollationCompareFunctorImpl(bool fCacheCollators) : |
| m_isValid(false), |
| m_defaultCollator(0), |
| m_defaultCollatorLocaleName(), |
| m_cacheCollators(fCacheCollators), |
| m_collatorCache() |
| { |
| UErrorCode theStatus = U_ZERO_ERROR; |
| |
| m_defaultCollator = createCollator(theStatus, &m_defaultCollatorLocaleName); |
| |
| if (U_SUCCESS(theStatus)) |
| { |
| m_isValid = true; |
| } |
| } |
| |
| |
| |
| ICUBridgeCollationCompareFunctorImpl::~ICUBridgeCollationCompareFunctorImpl() |
| { |
| XALAN_USING_STD(for_each) |
| |
| delete m_defaultCollator; |
| |
| for_each( |
| m_collatorCache.begin(), |
| m_collatorCache.end(), |
| CollationCacheStruct::CollatorDeleteFunctor()); |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::doCompare( |
| const CollatorType& theCollator, |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS) const |
| { |
| #if defined(XALAN_USE_WCHAR_CAST_HACK) |
| return theCollator.compare( |
| (const wchar_t*)theLHS, |
| length(theLHS), |
| (const wchar_t*)theRHS, |
| length(theRHS)); |
| #else |
| return theCollator.compare( |
| theLHS, |
| length(theLHS), |
| theRHS, |
| length(theRHS)); |
| #endif |
| } |
| |
| |
| |
| inline UColAttributeValue |
| caseOrderConvert(XalanCollationServices::eCaseOrder theCaseOrder) |
| { |
| switch(theCaseOrder) |
| { |
| case XalanCollationServices::eLowerFirst: |
| return UCOL_LOWER_FIRST; |
| break; |
| |
| case XalanCollationServices::eUpperFirst: |
| return UCOL_UPPER_FIRST; |
| break; |
| |
| case XalanCollationServices::eDefault: |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| return UCOL_DEFAULT; |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::doDefaultCompare( |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS) const |
| { |
| if (isValid() == false) |
| { |
| return s_defaultFunctor(theLHS, theRHS, XalanCollationServices::eDefault); |
| } |
| else |
| { |
| assert(m_defaultCollator != 0); |
| |
| return doCompare(*m_defaultCollator, theLHS, theRHS); |
| } |
| } |
| |
| |
| |
| inline ICUBridgeCollationCompareFunctorImpl::CollatorType* |
| createCollator( |
| const XalanDOMChar* theLocale, |
| UErrorCode& theStatus) |
| { |
| assert(theLocale != 0); |
| |
| const XalanDOMString::size_type theLength = length(theLocale); |
| |
| if (theLength >= ULOC_FULLNAME_CAPACITY) |
| { |
| theStatus = U_ILLEGAL_ARGUMENT_ERROR; |
| |
| return 0; |
| } |
| else |
| { |
| #if defined(XALAN_NON_ASCII_PLATFORM) |
| CharVectorType theVector; |
| |
| TranscodeToLocalCodePage(theLocale, theVector, true); |
| |
| const char* const theBuffer = c_str(theVector); |
| #else |
| char theBuffer[ULOC_FULLNAME_CAPACITY]; |
| |
| // Since language names must be ASCII, this |
| // is the cheapest way to "transcode"... |
| for (unsigned int i = 0; i <= theLength; ++i) |
| { |
| theBuffer[i] = char(theLocale[i]); |
| } |
| #endif |
| return ICUBridgeCollationCompareFunctorImpl::CollatorType::createInstance( |
| Locale::createFromName(theBuffer), |
| theStatus); |
| } |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::doCompare( |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS, |
| const XalanDOMChar* theLocale, |
| XalanCollationServices::eCaseOrder theCaseOrder) const |
| { |
| UErrorCode theStatus = U_ZERO_ERROR; |
| |
| XalanAutoPtr<CollatorType> theCollator(createCollator(theLocale, theStatus)); |
| |
| if (U_SUCCESS(theStatus)) |
| { |
| assert(theCollator.get() != 0); |
| |
| return doCompare( |
| *theCollator.get(), |
| theLHS, |
| theRHS, |
| theCaseOrder); |
| } |
| else |
| { |
| return s_defaultFunctor(theLHS, theRHS, theCaseOrder); |
| } |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::doCompareCached( |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS, |
| const XalanDOMChar* theLocale, |
| XalanCollationServices::eCaseOrder theCaseOrder) const |
| { |
| CollatorType* theCollator = getCachedCollator(theLocale); |
| |
| if (theCollator != 0) |
| { |
| return doCompare(*theCollator, theLHS, theRHS, theCaseOrder); |
| } |
| else |
| { |
| UErrorCode theStatus = U_ZERO_ERROR; |
| |
| XalanAutoPtr<CollatorType> theCollatorGuard(createCollator(theLocale, theStatus)); |
| |
| if (U_SUCCESS(theStatus)) |
| { |
| assert(theCollatorGuard.get() != 0); |
| |
| // OK, there was no error, so cache the instance... |
| cacheCollator(theCollatorGuard.get(), theLocale); |
| |
| // Release the collator, since it's in the cache and |
| // will be controlled by the cache... |
| theCollator = theCollatorGuard.release(); |
| |
| return doCompare( |
| *theCollator, |
| theLHS, |
| theRHS, |
| theCaseOrder); |
| } |
| else |
| { |
| return s_defaultFunctor(theLHS, theRHS, theCaseOrder); |
| } |
| } |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::doCompare( |
| CollatorType& theCollator, |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS, |
| XalanCollationServices::eCaseOrder theCaseOrder) const |
| { |
| UErrorCode theStatus = U_ZERO_ERROR; |
| |
| theCollator.setAttribute( |
| UCOL_CASE_FIRST, |
| caseOrderConvert(theCaseOrder), |
| theStatus); |
| |
| #if defined(XALAN_USE_WCHAR_CAST_HACK) |
| return theCollator.compare( |
| (const wchar_t*)theLHS, |
| length(theLHS), |
| (const wchar_t*)theRHS, |
| length(theRHS)); |
| #else |
| return theCollator.compare( |
| theLHS, |
| length(theLHS), |
| theRHS, |
| length(theRHS)); |
| #endif |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::operator()( |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS, |
| XalanCollationServices::eCaseOrder theCaseOrder) const |
| { |
| if (theCaseOrder == XalanCollationServices::eDefault) |
| { |
| return doDefaultCompare(theLHS, theRHS); |
| } |
| else |
| { |
| return doCompare( |
| theLHS, |
| theRHS, |
| c_wstr(m_defaultCollatorLocaleName), |
| theCaseOrder); |
| } |
| } |
| |
| |
| |
| int |
| ICUBridgeCollationCompareFunctorImpl::operator()( |
| const XalanDOMChar* theLHS, |
| const XalanDOMChar* theRHS, |
| const XalanDOMChar* theLocale, |
| XalanCollationServices::eCaseOrder theCaseOrder) const |
| { |
| if (theCaseOrder == XalanCollationServices::eDefault && |
| XalanDOMString::equals(m_defaultCollatorLocaleName, theLocale) == true) |
| { |
| return doDefaultCompare(theLHS, theRHS); |
| } |
| else if (m_cacheCollators == true) |
| { |
| return doCompareCached(theLHS, theRHS, theLocale, theCaseOrder); |
| } |
| else |
| { |
| return doCompare(theLHS, theRHS, theLocale, theCaseOrder); |
| }; |
| } |
| |
| |
| |
| ICUBridgeCollationCompareFunctorImpl::CollatorType* |
| ICUBridgeCollationCompareFunctorImpl::getCachedCollator(const XalanDOMChar* theLocale) const |
| { |
| XALAN_USING_STD(find_if) |
| |
| CollatorCacheListType& theNonConstCache = |
| #if defined(XALAN_NO_MUTABLE) |
| (CollatorCacheListType&)m_collatorCache; |
| #else |
| m_collatorCache; |
| #endif |
| |
| CollatorCacheListType::iterator i = |
| find_if( |
| theNonConstCache.begin(), |
| theNonConstCache.end(), |
| CollationCacheStruct::CollatorFindFunctor(theLocale)); |
| |
| if (i == theNonConstCache.end()) |
| { |
| return 0; |
| } |
| else |
| { |
| // Let's do a quick check to see if we found the first entry. |
| // If so, we don't have to update the cache, so just return the |
| // appropriate value... |
| const CollatorCacheListType::iterator theBegin = |
| theNonConstCache.begin(); |
| |
| if (i == theBegin) |
| { |
| return (*i).m_collator; |
| } |
| else |
| { |
| // Save the collator, because splice() may invalidate |
| // i. |
| CollatorType* const theCollator = (*i).m_collator; |
| |
| // Move the entry to the beginning the cache |
| theNonConstCache.splice(theBegin, theNonConstCache, i); |
| |
| return theCollator; |
| } |
| } |
| } |
| |
| |
| |
| void |
| ICUBridgeCollationCompareFunctorImpl::cacheCollator( |
| CollatorType* theCollator, |
| const XalanDOMChar* theLocale) const |
| { |
| assert(theCollator != 0); |
| assert(theLocale != 0); |
| |
| CollatorCacheListType& theNonConstCache = |
| #if defined(XALAN_NO_MUTABLE) |
| (CollatorCacheListType&)m_collatorCache; |
| #else |
| m_collatorCache; |
| #endif |
| |
| // Is the cache full? |
| if (theNonConstCache.size() == eCacheMax) |
| { |
| // Yes, so guard the collator instance, in case pop_back() throws... |
| XalanAutoPtr<CollatorType> theCollatorGuard(theNonConstCache.back().m_collator); |
| |
| theNonConstCache.pop_back(); |
| } |
| |
| theNonConstCache.push_front(CollatorCacheListType::value_type()); |
| |
| CollatorCacheListType::value_type& theEntry = |
| theNonConstCache.front(); |
| |
| // Set the locale first, since that might throw an exception... |
| theEntry.m_locale = theLocale; |
| |
| theEntry.m_collator = theCollator; |
| } |
| |
| |
| |
| XALAN_CPP_NAMESPACE_END |