diff --git a/icu4c/source/allinone/allinone.sln b/icu4c/source/allinone/allinone.sln index 5c718d2e27b..d441b581530 100644 --- a/icu4c/source/allinone/allinone.sln +++ b/icu4c/source/allinone/allinone.sln @@ -139,7 +139,6 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedata", "..\data\makedata.vcproj", "{D9DF7F2F-93B7-4810-B5CD-96F4F33C079B}" ProjectSection(ProjectDependencies) = postProject {C2BE5000-7501-4E87-9724-B8D82494FAE6} = {C2BE5000-7501-4E87-9724-B8D82494FAE6} - {F5213103-6CBE-46E6-B4CC-2570B6837D86} = {F5213103-6CBE-46E6-B4CC-2570B6837D86} {F5281B04-A9E0-4680-BBA8-1D7F7D115458} = {F5281B04-A9E0-4680-BBA8-1D7F7D115458} {97521D06-EC47-45D4-8BD0-9E16B3F93B2A} = {97521D06-EC47-45D4-8BD0-9E16B3F93B2A} {C2B04507-2521-4801-BF0D-5FD79D6D518C} = {C2B04507-2521-4801-BF0D-5FD79D6D518C} @@ -156,6 +155,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedata", "..\data\makedat {73C0A65B-D1F2-4DE1-B3A6-15DAD2C23F3D} = {73C0A65B-D1F2-4DE1-B3A6-15DAD2C23F3D} {62D4B15D-7A90-4ECB-BA19-5E021D6A21BC} = {62D4B15D-7A90-4ECB-BA19-5E021D6A21BC} {73632960-B3A6-464D-83A3-4B43365F19B8} = {73632960-B3A6-464D-83A3-4B43365F19B8} + {C7891A65-80AB-4245-912E-5F1E17B0E6C4} = {C7891A65-80AB-4245-912E-5F1E17B0E6C4} {77C78066-746F-4EA6-B3FE-B8C8A4A97891} = {77C78066-746F-4EA6-B3FE-B8C8A4A97891} {37FC2C7F-1904-4811-8955-2F478830EAD1} = {37FC2C7F-1904-4811-8955-2F478830EAD1} {E4993E82-D68A-46CA-BAE0-9D35E172E46F} = {E4993E82-D68A-46CA-BAE0-9D35E172E46F} @@ -280,10 +280,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testplug", "..\tools\icuinf EndProjectSection EndProject Global - GlobalSection(SubversionScc) = preSolution - Svn-Managed = True - Manager = AnkhSVN - Subversion Support for Visual Studio - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 @@ -607,4 +603,8 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(SubversionScc) = preSolution + Svn-Managed = True + Manager = AnkhSVN - Subversion Support for Visual Studio + EndGlobalSection EndGlobal diff --git a/icu4c/source/common/hash.h b/icu4c/source/common/hash.h index 375499bf1b8..9fedd0e521f 100644 --- a/icu4c/source/common/hash.h +++ b/icu4c/source/common/hash.h @@ -1,6 +1,6 @@ /* ****************************************************************************** -* Copyright (C) 1997-2006, International Business Machines +* Copyright (C) 1997-2010, International Business Machines * Corporation and others. All Rights Reserved. ****************************************************************************** * Date Name Description @@ -40,8 +40,8 @@ public: /** * Construct a hashtable - * @param keyComp Compartor for comparing the keys - * @param valueComp Compartor for comparing the values + * @param keyComp Comparator for comparing the keys + * @param valueComp Comparator for comparing the values * @param status Error code */ Hashtable(UKeyComparator *keyComp, UValueComparator *valueComp, UErrorCode& status); @@ -86,9 +86,9 @@ public: const UHashElement* nextElement(int32_t& pos) const; - UKeyComparator* setKeyCompartor(UKeyComparator*keyComp); + UKeyComparator* setKeyComparator(UKeyComparator*keyComp); - UValueComparator* setValueCompartor(UValueComparator* valueComp); + UValueComparator* setValueComparator(UValueComparator* valueComp); UBool equals(const Hashtable& that) const; private: @@ -190,11 +190,11 @@ inline void Hashtable::removeAll(void) { uhash_removeAll(hash); } -inline UKeyComparator* Hashtable::setKeyCompartor(UKeyComparator*keyComp){ +inline UKeyComparator* Hashtable::setKeyComparator(UKeyComparator*keyComp){ return uhash_setKeyComparator(hash, keyComp); } -inline UValueComparator* Hashtable::setValueCompartor(UValueComparator* valueComp){ +inline UValueComparator* Hashtable::setValueComparator(UValueComparator* valueComp){ return uhash_setValueComparator(hash, valueComp); } diff --git a/icu4c/source/common/normalizer2.cpp b/icu4c/source/common/normalizer2.cpp index 90512d7b743..a84d1e34a05 100644 --- a/icu4c/source/common/normalizer2.cpp +++ b/icu4c/source/common/normalizer2.cpp @@ -27,6 +27,7 @@ #include "mutex.h" #include "normalizer2impl.h" #include "ucln_cmn.h" +#include "uhash.h" U_NAMESPACE_BEGIN @@ -409,13 +410,21 @@ private: STATIC_SIMPLE_SINGLETON(noopSingleton); +static UHashtable *cache=NULL; + U_CDECL_BEGIN +static void U_CALLCONV deleteNorm2AllModes(void *allModes) { + delete (Norm2AllModes *)allModes; +} + static UBool U_CALLCONV uprv_normalizer2_cleanup() { Norm2AllModesSingleton(nfcSingleton, NULL).deleteInstance(); Norm2AllModesSingleton(nfkcSingleton, NULL).deleteInstance(); Norm2AllModesSingleton(nfkc_cfSingleton, NULL).deleteInstance(); Norm2Singleton(noopSingleton).deleteInstance(); + uhash_close(cache); + cache=NULL; return TRUE; } @@ -534,8 +543,11 @@ Normalizer2::getInstance(const char *packageName, if(U_FAILURE(errorCode)) { return NULL; } + if(name==NULL || *name==0) { + errorCode=U_ILLEGAL_ARGUMENT_ERROR; + } + Norm2AllModes *allModes=NULL; if(packageName==NULL) { - Norm2AllModes *allModes=NULL; if(0==uprv_strcmp(name, "nfc")) { allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode); } else if(0==uprv_strcmp(name, "nfkc")) { @@ -543,25 +555,64 @@ Normalizer2::getInstance(const char *packageName, } else if(0==uprv_strcmp(name, "nfkc_cf")) { allModes=Norm2AllModesSingleton(nfkc_cfSingleton, "nfkc_cf").getInstance(errorCode); } - if(allModes!=NULL) { - switch(mode) { - case UNORM2_COMPOSE: - return &allModes->comp; - case UNORM2_DECOMPOSE: - return &allModes->decomp; - case UNORM2_FCD: - allModes->impl.getFCDTrie(errorCode); - return &allModes->fcd; - case UNORM2_COMPOSE_CONTIGUOUS: - return &allModes->fcc; - default: - break; // do nothing + } + if(allModes==NULL && U_SUCCESS(errorCode)) { + UHashtable *localCache; + { + Mutex lock; + localCache=cache; + if(localCache!=NULL) { + allModes=(Norm2AllModes *)uhash_get(localCache, name); + } + } + if(allModes==NULL) { + if(localCache==NULL) { + Mutex lock; + if(cache==NULL) { + cache=uhash_open(uhash_hashChars, uhash_compareChars, NULL, &errorCode); + if(U_FAILURE(errorCode)) { + return NULL; + } + uhash_setKeyDeleter(cache, uprv_free); + uhash_setValueDeleter(cache, deleteNorm2AllModes); + } + localCache=cache; + } + allModes=Norm2AllModes::createInstance(packageName, name, errorCode); + if(U_SUCCESS(errorCode)) { + Mutex lock; + void *temp=uhash_get(localCache, name); + if(temp==NULL) { + int32_t keyLength=uprv_strlen(name)+1; + char *nameCopy=(char *)uprv_malloc(keyLength); + if(nameCopy==NULL) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + uprv_memcpy(nameCopy, name, keyLength); + uhash_put(localCache, nameCopy, allModes, &errorCode); + } else { + // race condition + delete allModes; + allModes=(Norm2AllModes *)temp; + } } } } - if(U_SUCCESS(errorCode)) { - // TODO: Real loading and caching... - errorCode=U_UNSUPPORTED_ERROR; + if(allModes!=NULL && U_SUCCESS(errorCode)) { + switch(mode) { + case UNORM2_COMPOSE: + return &allModes->comp; + case UNORM2_DECOMPOSE: + return &allModes->decomp; + case UNORM2_FCD: + allModes->impl.getFCDTrie(errorCode); + return &allModes->fcd; + case UNORM2_COMPOSE_CONTIGUOUS: + return &allModes->fcc; + default: + break; // do nothing + } } return NULL; } diff --git a/icu4c/source/i18n/currpinf.cpp b/icu4c/source/i18n/currpinf.cpp index 6f37eac37b3..8087d4be582 100644 --- a/icu4c/source/i18n/currpinf.cpp +++ b/icu4c/source/i18n/currpinf.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* - * Copyright (C) 2009, International Business Machines Corporation and * - * others. All Rights Reserved. * + * Copyright (C) 2009-2010, International Business Machines Corporation and + * others. All Rights Reserved. ******************************************************************************* */ @@ -347,7 +347,7 @@ CurrencyPluralInfo::initHash(UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - hTable->setValueCompartor(ValueComparator); + hTable->setValueComparator(ValueComparator); return hTable; } diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 0606214f965..be96b982a12 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -4604,7 +4604,7 @@ DecimalFormat::initHashForAffix(UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - hTable->setValueCompartor(decimfmtAffixValueComparator); + hTable->setValueComparator(decimfmtAffixValueComparator); return hTable; } @@ -4618,7 +4618,7 @@ DecimalFormat::initHashForAffixPattern(UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - hTable->setValueCompartor(decimfmtAffixPatternValueComparator); + hTable->setValueComparator(decimfmtAffixPatternValueComparator); return hTable; } diff --git a/icu4c/source/i18n/dtitvinf.cpp b/icu4c/source/i18n/dtitvinf.cpp index dc15e9135f4..6aca48ee570 100644 --- a/icu4c/source/i18n/dtitvinf.cpp +++ b/icu4c/source/i18n/dtitvinf.cpp @@ -602,7 +602,7 @@ DateIntervalInfo::initHash(UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - hTable->setValueCompartor(dtitvinfHashTableValueComparator); + hTable->setValueComparator(dtitvinfHashTableValueComparator); return hTable; } diff --git a/icu4c/source/i18n/tmutfmt.cpp b/icu4c/source/i18n/tmutfmt.cpp index 5913be32251..47d6859b7d2 100644 --- a/icu4c/source/i18n/tmutfmt.cpp +++ b/icu4c/source/i18n/tmutfmt.cpp @@ -822,7 +822,7 @@ TimeUnitFormat::initHash(UErrorCode& status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - hTable->setValueCompartor(tmutfmtHashTableValueComparator); + hTable->setValueComparator(tmutfmtHashTableValueComparator); return hTable; } diff --git a/icu4c/source/test/intltest/tstnorm.cpp b/icu4c/source/test/intltest/tstnorm.cpp index 3c9b3713aef..6ec969d8201 100644 --- a/icu4c/source/test/intltest/tstnorm.cpp +++ b/icu4c/source/test/intltest/tstnorm.cpp @@ -9,6 +9,7 @@ #if !UCONFIG_NO_NORMALIZATION #include "unicode/uchar.h" +#include "unicode/errorcode.h" #include "unicode/normlzr.h" #include "unicode/uniset.h" #include "unicode/usetiter.h" @@ -51,6 +52,7 @@ void BasicNormalizerTest::runIndexedTest(int32_t index, UBool exec, CASE(14,FindFoldFCDExceptions); CASE(15,TestCompare); CASE(16,TestSkippable); + CASE(17,TestCustomComp); default: name = ""; break; } } @@ -1754,4 +1756,37 @@ BasicNormalizerTest::TestSkippable() { } } +struct StringPair { const char *input, *expected; }; + +void +BasicNormalizerTest::TestCustomComp() { + static const StringPair pairs[]={ + { "\\uD801\\uE000\\uDFFE", "" }, + { "\\uD800\\uD801\\uE000\\uDFFE\\uDFFF", "\\uD7FF\\uFFFF" }, + { "\\uD800\\uD801\\uDFFE\\uDFFF", "\\uD7FF\\U000107FE\\uFFFF" }, + { "\\uE001\\U000110B9\\u0345\\u0308\\u0327", "\\uE002\\U000110B9\\u0327\\u0345" }, + { "\\uE010\\U000F0011\\uE012", "\\uE011\\uE012" }, + { "\\uE010\\U000F0011\\U000F0011\\uE012", "\\uE011\\U000F0010" }, + { "\\uE111\\u1161\\uE112\\u1162", "\\uAE4C\\u1102\\u0062\\u1162" }, + { "\\uFFF3\\uFFF7\\U00010036\\U00010077", "\\U00010037\\U00010037\\uFFF6\\U00010037" } + }; + IcuTestErrorCode errorCode(*this, "BasicNormalizerTest/TestCustomComp"); + const Normalizer2 *customComp= + Normalizer2::getInstance(loadTestData(errorCode), "testnorm", UNORM2_COMPOSE, errorCode); + if(errorCode.isFailure()) { + errorCode.reset(); + dataerrln(UNICODE_STRING_SIMPLE("unable to load testdata/testnorm.nrm")); + return; + } + for(int32_t i=0; inormalize(input, errorCode); + if(result!=expected) { + errln("custom compose Normalizer2 did not normalize input %d as expected", i); + } + } +} + #endif /* #if !UCONFIG_NO_NORMALIZATION */ diff --git a/icu4c/source/test/intltest/tstnorm.h b/icu4c/source/test/intltest/tstnorm.h index b77efd6bf2a..132551c969a 100644 --- a/icu4c/source/test/intltest/tstnorm.h +++ b/icu4c/source/test/intltest/tstnorm.h @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2003, International Business Machines Corporation and + * Copyright (c) 1997-2010, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -42,6 +42,7 @@ public: void TestCompare(void); void FindFoldFCDExceptions(); void TestSkippable(); + void TestCustomComp(); private: UnicodeString canonTests[24][3]; diff --git a/icu4c/source/test/intltest/uvectest.cpp b/icu4c/source/test/intltest/uvectest.cpp index c2ec035993e..98b2f38c021 100644 --- a/icu4c/source/test/intltest/uvectest.cpp +++ b/icu4c/source/test/intltest/uvectest.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 2004-2009, International Business Machines Corporation and + * Copyright (c) 2004-2010, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -178,21 +178,21 @@ void UVectorTest::Hashtable_API() { TEST_ASSERT((a->removei("a") == 1)); TEST_ASSERT((a->find("a") == NULL)); - /* verify that setValueCompartor works */ + /* verify that setValueComparator works */ Hashtable b(status); TEST_ASSERT((!a->equals(b))); TEST_ASSERT((b.puti("b", 2, status) == 0)); TEST_ASSERT((!a->equals(b))); // Without a value comparator, this will be FALSE by default. - b.setValueCompartor(uhash_compareLong); + b.setValueComparator(uhash_compareLong); TEST_ASSERT((!a->equals(b))); - a->setValueCompartor(uhash_compareLong); + a->setValueComparator(uhash_compareLong); TEST_ASSERT((a->equals(b))); TEST_ASSERT((a->equals(*a))); // This better be reflexive. - /* verify that setKeyCompartor works */ + /* verify that setKeyComparator works */ TEST_ASSERT((a->puti("a", 1, status) == 0)); TEST_ASSERT((a->find("a") != NULL)); - a->setKeyCompartor(neverTRUE); + a->setKeyComparator(neverTRUE); TEST_ASSERT((a->find("a") == NULL)); delete a; diff --git a/icu4c/source/test/testdata/Makefile.in b/icu4c/source/test/testdata/Makefile.in index bbd8636195e..cf8eaed8cf8 100644 --- a/icu4c/source/test/testdata/Makefile.in +++ b/icu4c/source/test/testdata/Makefile.in @@ -123,6 +123,8 @@ TEST_UCM_SOURCE= test1.ucm test1bmp.ucm test3.ucm test4.ucm test4x.ucm test5.ucm TEST_UCM_FILES=$(TEST_UCM_SOURCE:%=$(TESTSRCDATADIR)/data/%) TEST_CNV_FILES=$(TEST_UCM_SOURCE:%.ucm=$(TESTBUILDDIR)/%.cnv) +TEST_NRM_FILES=$(TESTBUILDDIR)/testnorm.nrm + # import the shared .mk file include $(TESTSRCDATADIR)/tstfiles.mk -include $(TESTSRCDATADIR)/tstlocal.mk @@ -138,7 +140,7 @@ ALL_TEST_FILES = $(TEST_DAT_FILES) $(TEST_SPP_FILES) $(TEST_BRK_FILES) $(TEST_CN $(TESTBUILDDIR)/testdata.lst: $(SRCLISTDEPS) @echo "generating $@ (list of data files)" @-$(RMV) $@ - @for file in $(TEST_RES_FILES:$(TESTBUILDDIR)/%.res=%.res) $(TEST_DAT_FILES:$(TESTBUILDDIR)/%.icu=%.icu) $(TEST_SPP_FILES:$(TESTBUILDDIR)/%.spp=%.spp) $(TEST_CNV_FILES:$(TESTBUILDDIR)/%.cnv=%.cnv); do \ + @for file in $(TEST_RES_FILES:$(TESTBUILDDIR)/%.res=%.res) $(TEST_DAT_FILES:$(TESTBUILDDIR)/%.icu=%.icu) $(TEST_SPP_FILES:$(TESTBUILDDIR)/%.spp=%.spp) $(TEST_CNV_FILES:$(TESTBUILDDIR)/%.cnv=%.cnv) $(TEST_NRM_FILES:$(TESTBUILDDIR)/%.nrm=%.nrm); do \ echo $$file >> $@; \ done; @@ -183,6 +185,9 @@ $(TESTBUILDDIR)/nfsmxp.spp: $(TOOLBINDIR)/gensprep$(EXEEXT) $(TESTSRCDATADIR)/nf $(TESTBUILDDIR)/%.cnv: $(TESTSRCDATADIR)/%.ucm $(TOOLBINDIR)/makeconv$(EXEEXT) $(INVOKE) $(TOOLBINDIR)/makeconv --small -c -d $(TESTBUILDDIR) $(TESTSRCDATADIR)/$(D7FF # surrogates with mappings, and mappings to empty strings +D801> +DFFE> +DFFF>FFFF +E000> +E001=61 338 # composition with trail<=33FF and composite>7FFF +E002=E001 308 # recursive mapping needs reordering +E003>62 307 327 337 # mapping needs reordering +E011=E010 F0011 # composition of BMP+supplementary, and F0011 is maybe & combines-fwd +E111>1101 # mapping ends in Jamo L +E112>1102 62 # mapping starts with Jamo L +FFF3>FFF4 +FFF4>FFF5 +FFF5>FFF7 +FFF7>10037 +10036>FFF6 +10077>10037 +1109A=11099 110BA +1109C=1109B 110BA +110AB=110A5 110BA +F0010=F0011 E012 # composition of supplementary+BMP