diff --git a/icu4c/source/common/Makefile.in b/icu4c/source/common/Makefile.in
index 36e3293eb4b..bf553105123 100644
--- a/icu4c/source/common/Makefile.in
+++ b/icu4c/source/common/Makefile.in
@@ -1,6 +1,6 @@
#******************************************************************************
#
-# Copyright (C) 1999-2004, International Business Machines
+# Copyright (C) 1999-2005, International Business Machines
# Corporation and others. All Rights Reserved.
#
#******************************************************************************
@@ -67,7 +67,7 @@ ucnv.o ucnv_set.o ucnv_bld.o ucnv_cb.o ucnv_cnv.o ucnv_err.o ucnv_ext.o ucnv_io.
ucnv_u7.o ucnv_u8.o ucnv_u16.o ucnv_u32.o ucnvscsu.o ucnvbocu.o \
ucnvmbcs.o ucnv2022.o ucnvhz.o ucnv_lmb.o ucnvisci.o ucnvdisp.o \
unistr.o unistr_case.o unistr_cnv.o unistr_props.o \
-utf_impl.o ustring.o ustr_cnv.o ustrcase.o cstring.o ustrfmt.o ustrtrns.o ustr_wcs.o uinvchar.o \
+utf_impl.o ustring.o ustr_cnv.o ustrcase.o ucasemap.o cstring.o ustrfmt.o ustrtrns.o ustr_wcs.o uinvchar.o \
normlzr.o unorm.o unormcmp.o unorm_it.o chariter.o schriter.o uchriter.o uiter.o \
uchar.o uprops.o ucase.o propname.o ubidi_props.o ubidi.o ubidiwrt.o ubidiln.o ushape.o unames.o \
uscript.o usc_impl.o uvector.o ustack.o uvectr32.o ucmp8.o \
diff --git a/icu4c/source/common/common.vcproj b/icu4c/source/common/common.vcproj
index ef53007bac7..bc373e5da41 100644
--- a/icu4c/source/common/common.vcproj
+++ b/icu4c/source/common/common.vcproj
@@ -1751,6 +1751,26 @@
Outputs="..\..\include\unicode\$(InputFileName)"/>
+
+
+
+
+
+
+
+
+
+
diff --git a/icu4c/source/common/ucase.c b/icu4c/source/common/ucase.c
index 8620a38533d..8ea1488786b 100644
--- a/icu4c/source/common/ucase.c
+++ b/icu4c/source/common/ucase.c
@@ -918,13 +918,13 @@ enum {
/* separator? */
#define is_sep(c) ((c)=='_' || (c)=='-' || (c)==0)
-/*
+/**
* Requires non-NULL locale ID but otherwise does the equivalent of
* checking for language codes as if uloc_getLanguage() were called:
* Accepts both 2- and 3-letter codes and accepts case variants.
*/
-static int32_t
-getCaseLocale(const char *locale, int32_t *locCache) {
+U_CFUNC int32_t
+ucase_getCaseLocale(const char *locale, int32_t *locCache) {
int32_t result;
char c;
@@ -1171,7 +1171,7 @@ ucase_toFullLower(const UCaseProps *csp, UChar32 c,
if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) {
/* use hardcoded conditions and mappings */
- int32_t loc=getCaseLocale(locale, locCache);
+ int32_t loc=ucase_getCaseLocale(locale, locCache);
/*
* Test for conditional mappings first
@@ -1318,7 +1318,7 @@ toUpperOrTitle(const UCaseProps *csp, UChar32 c,
if(excWord&UCASE_EXC_CONDITIONAL_SPECIAL) {
/* use hardcoded conditions and mappings */
- int32_t loc=getCaseLocale(locale, locCache);
+ int32_t loc=ucase_getCaseLocale(locale, locCache);
if(loc==LOC_TURKISH && c==0x69) {
/*
diff --git a/icu4c/source/common/ucase.h b/icu4c/source/common/ucase.h
index 999287ebab8..5781756692f 100644
--- a/icu4c/source/common/ucase.h
+++ b/icu4c/source/common/ucase.h
@@ -61,6 +61,14 @@ ucase_swap(const UDataSwapper *ds,
U_CAPI void U_EXPORT2
ucase_addPropertyStarts(const UCaseProps *csp, const USetAdder *sa, UErrorCode *pErrorCode);
+/**
+ * Requires non-NULL locale ID but otherwise does the equivalent of
+ * checking for language codes as if uloc_getLanguage() were called:
+ * Accepts both 2- and 3-letter codes and accepts case variants.
+ */
+U_CFUNC int32_t
+ucase_getCaseLocale(const char *locale, int32_t *locCache);
+
/**
* Bit mask for getting just the options from a string compare options word
* that are relevant for case-insensitive string comparison.
diff --git a/icu4c/source/common/ucasemap.c b/icu4c/source/common/ucasemap.c
new file mode 100644
index 00000000000..02f94762815
--- /dev/null
+++ b/icu4c/source/common/ucasemap.c
@@ -0,0 +1,333 @@
+/*
+*******************************************************************************
+*
+* Copyright (C) 2005, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+*******************************************************************************
+* file name: ucasemap.c
+* encoding: US-ASCII
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 2005may06
+* created by: Markus W. Scherer
+*
+* Case mapping service object and functions using it.
+*/
+
+#include "unicode/utypes.h"
+#include "unicode/uloc.h"
+#include "unicode/ustring.h"
+#include "unicode/ucasemap.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "ucase.h"
+#include "ustr_imp.h"
+
+/* UCaseMap service object -------------------------------------------------- */
+
+struct UCaseMap {
+ const UCaseProps *csp;
+ char locale[32];
+ int32_t locCache;
+ uint32_t options;
+};
+
+U_DRAFT UCaseMap * U_EXPORT2
+ucasemap_open(const char *locale, uint32_t options, UErrorCode *pErrorCode) {
+ UCaseMap *csm;
+
+ if(U_FAILURE(*pErrorCode)) {
+ return NULL;
+ }
+
+ csm=(UCaseMap *)uprv_malloc(sizeof(UCaseMap));
+ if(csm==NULL) {
+ return NULL;
+ }
+ uprv_memset(csm, 0, sizeof(UCaseMap));
+
+ csm->csp=ucase_getSingleton(pErrorCode);
+ ucasemap_setLocale(csm, locale, pErrorCode);
+ if(U_FAILURE(*pErrorCode)) {
+ uprv_free(csm);
+ return NULL;
+ }
+
+ csm->options=options;
+ return csm;
+}
+
+U_DRAFT void U_EXPORT2
+ucasemap_close(UCaseMap *csm) {
+ if(csm!=NULL) {
+ uprv_free(csm);
+ }
+}
+
+U_DRAFT const char * U_EXPORT2
+ucasemap_getLocale(const UCaseMap *csm) {
+ return csm->locale;
+}
+
+U_DRAFT uint32_t U_EXPORT2
+ucasemap_getOptions(const UCaseMap *csm) {
+ return csm->options;
+}
+
+U_DRAFT void U_EXPORT2
+ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode) {
+ int32_t length;
+
+ if(U_FAILURE(*pErrorCode)) {
+ return;
+ }
+
+ length=uloc_getName(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode);
+ if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR || length==sizeof(csm->locale)) {
+ *pErrorCode=U_ZERO_ERROR;
+ /* we only really need the language code for case mappings */
+ length=uloc_getLanguage(locale, csm->locale, (int32_t)sizeof(csm->locale), pErrorCode);
+ }
+ if(length==sizeof(csm->locale)) {
+ *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
+ }
+ csm->locCache=0;
+ if(U_SUCCESS(*pErrorCode)) {
+ ucase_getCaseLocale(csm->locale, &csm->locCache);
+ } else {
+ csm->locale[0]=0;
+ }
+}
+
+U_DRAFT void U_EXPORT2
+ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode) {
+ csm->options=options;
+}
+
+/* UTF-8 string case mappings ----------------------------------------------- */
+
+/* append a full case mapping result, see UCASE_MAX_STRING_LENGTH */
+static U_INLINE int32_t
+appendResult(uint8_t *dest, int32_t destIndex, int32_t destCapacity,
+ int32_t result, const UChar *s) {
+ UChar32 c;
+ int32_t length, destLength;
+ UErrorCode errorCode;
+
+ /* decode the result */
+ if(result<0) {
+ /* (not) original code point */
+ c=~result;
+ length=-1;
+ } else if(result<=UCASE_MAX_STRING_LENGTH) {
+ c=U_SENTINEL;
+ length=result;
+ } else {
+ c=result;
+ length=-1;
+ }
+
+ if(destIndexindex=csc->cpStart;
+ csc->dir=dir;
+ } else if(dir>0) {
+ /* reset for forward iteration */
+ csc->index=csc->cpLimit;
+ csc->dir=dir;
+ } else {
+ /* continue current iteration direction */
+ dir=csc->dir;
+ }
+
+ if(dir<0) {
+ if(csc->startindex) {
+ U8_PREV((const uint8_t *)csc->p, csc->start, csc->index, c);
+ return c;
+ }
+ } else {
+ if(csc->indexlimit) {
+ U8_NEXT((const uint8_t *)csc->p, csc->index, csc->limit, c);
+ return c;
+ }
+ }
+ return U_SENTINEL;
+}
+
+typedef int32_t U_CALLCONV
+UCaseMapFull(const UCaseProps *csp, UChar32 c,
+ UCaseContextIterator *iter, void *context,
+ const UChar **pString,
+ const char *locale, int32_t *locCache);
+
+/*
+ * Case-maps [srcStart..srcLimit[ but takes
+ * context [0..srcLength[ into account.
+ */
+static int32_t
+_caseMap(const UCaseMap *csm, UCaseMapFull *map,
+ uint8_t *dest, int32_t destCapacity,
+ const uint8_t *src, UCaseContext *csc,
+ int32_t srcStart, int32_t srcLimit,
+ UErrorCode *pErrorCode) {
+ const UChar *s;
+ UChar32 c;
+ int32_t srcIndex, destIndex;
+ int32_t locCache;
+
+ locCache=csm->locCache;
+
+ /* case mapping loop */
+ srcIndex=srcStart;
+ destIndex=0;
+ while(srcIndexcpStart=srcIndex;
+ U8_NEXT(src, srcIndex, srcLimit, c);
+ csc->cpLimit=srcIndex;
+ c=map(csm->csp, c, utf8_caseContextIterator, csc, &s, csm->locale, &locCache);
+ destIndex=appendResult(dest, destIndex, destCapacity, c, s);
+ }
+
+ if(destIndex>destCapacity) {
+ *pErrorCode=U_BUFFER_OVERFLOW_ERROR;
+ }
+ return destIndex;
+}
+
+/*
+ * Implement argument checking and buffer handling
+ * for string case mapping as a common function.
+ */
+enum {
+ TO_LOWER,
+ TO_UPPER,
+ TO_TITLE,
+ FOLD_CASE
+};
+
+/* common internal function for public API functions */
+
+static int32_t
+caseMap(const UCaseMap *csm,
+ uint8_t *dest, int32_t destCapacity,
+ const uint8_t *src, int32_t srcLength,
+ int32_t toWhichCase,
+ UErrorCode *pErrorCode) {
+ UCaseContext csc={ NULL };
+ int32_t destLength;
+
+ /* check argument values */
+ if(U_FAILURE(*pErrorCode)) {
+ return 0;
+ }
+ if( destCapacity<0 ||
+ (dest==NULL && destCapacity>0) ||
+ src==NULL ||
+ srcLength<-1
+ ) {
+ *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ /* get the string length */
+ if(srcLength==-1) {
+ srcLength=uprv_strlen((const char *)src);
+ }
+
+ /* check for overlapping source and destination */
+ if( dest!=NULL &&
+ ((src>=dest && src<(dest+destCapacity)) ||
+ (dest>=src && dest<(src+srcLength)))
+ ) {
+ *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ destLength=0;
+
+ csc.p=(void *)src;
+ csc.limit=srcLength;
+
+ if(toWhichCase==TO_LOWER) {
+ destLength=_caseMap(csm, ucase_toFullLower,
+ dest, destCapacity,
+ src, &csc,
+ 0, srcLength,
+ pErrorCode);
+ } else /* if(toWhichCase==TO_UPPER) */ {
+ destLength=_caseMap(csm, ucase_toFullUpper,
+ dest, destCapacity,
+ src, &csc,
+ 0, srcLength,
+ pErrorCode);
+ }
+
+ return u_terminateChars((char *)dest, destCapacity, destLength, pErrorCode);
+}
+
+/* public API functions */
+
+U_DRAFT int32_t U_EXPORT2
+ucasemap_utf8ToLower(const UCaseMap *csm,
+ char *dest, int32_t destCapacity,
+ const char *src, int32_t srcLength,
+ UErrorCode *pErrorCode) {
+ return caseMap(csm,
+ (uint8_t *)dest, destCapacity,
+ (const uint8_t *)src, srcLength,
+ TO_LOWER, pErrorCode);
+}
+
+U_DRAFT int32_t U_EXPORT2
+ucasemap_utf8ToUpper(const UCaseMap *csm,
+ char *dest, int32_t destCapacity,
+ const char *src, int32_t srcLength,
+ UErrorCode *pErrorCode) {
+ return caseMap(csm,
+ (uint8_t *)dest, destCapacity,
+ (const uint8_t *)src, srcLength,
+ TO_UPPER, pErrorCode);
+}
diff --git a/icu4c/source/common/unicode/ucasemap.h b/icu4c/source/common/unicode/ucasemap.h
new file mode 100644
index 00000000000..17c8f601f75
--- /dev/null
+++ b/icu4c/source/common/unicode/ucasemap.h
@@ -0,0 +1,180 @@
+/*
+*******************************************************************************
+*
+* Copyright (C) 2005, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+*******************************************************************************
+* file name: ucasemap.h
+* encoding: US-ASCII
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 2005may06
+* created by: Markus W. Scherer
+*
+* Case mapping service object and functions using it.
+*/
+
+#ifndef __UCASEMAP_H__
+#define __UCASEMAP_H__
+
+#include "unicode/utypes.h"
+#include "unicode/ustring.h"
+
+/**
+ * \file
+ * \brief C API: Unicode case mapping functions using a UCaseMap service object.
+ *
+ * The service object takes care of memory allocations, data loading, and setup
+ * for the attributes, as usual.
+ *
+ * Currently, the functionality provided here does not overlap with uchar.h
+ * and ustring.h.
+ *
+ * ucasemap_utf8ToLower() and ucasemap_utf8ToUpper() operate directly on
+ * UTF-8 strings.
+ */
+
+/**
+ * UCaseMap is an opaque service object for newer ICU case mapping functions.
+ * Older functions did not use a service object.
+ * @draft ICU 3.4
+ */
+struct UCaseMap;
+typedef struct UCaseMap UCaseMap; /**< C typedef for struct UCaseMap. @draft ICU 3.4 */
+
+/**
+ * Open a UCaseMap service object for a locale and a set of options.
+ * The locale ID and options are preprocessed so that functions using the
+ * service object need not process them in each call.
+ *
+ * @param locale ICU locale ID, used for language-dependent
+ * upper-/lower-/title-casing according to the Unicode standard.
+ * Usual semantics: ""=root, NULL=default locale, etc.
+ * @param options Options bit set, used for case folding and string comparisons.
+ * Same flags as for u_foldCase(), u_strFoldCase(),
+ * u_strCaseCompare(), etc.
+ * Use 0 or U_FOLD_CASE_DEFAULT for default behavior.
+ * @param pErrorCode Must be a valid pointer to an error code value,
+ * which must not indicate a failure before the function call.
+ * @return Pointer to a UCaseMap service object, if successful.
+ *
+ * @draft ICU 3.4
+ */
+U_DRAFT UCaseMap * U_EXPORT2
+ucasemap_open(const char *locale, uint32_t options, UErrorCode *pErrorCode);
+
+/**
+ * Close a UCaseMap service object.
+ * @param csm Object to be closed.
+ * @draft ICU 3.4
+ */
+U_DRAFT void U_EXPORT2
+ucasemap_close(UCaseMap *csm);
+
+/**
+ * Get the locale ID that is used for language-dependent case mappings.
+ * @param csm UCaseMap service object.
+ * @return locale ID
+ * @draft ICU 3.4
+ */
+U_DRAFT const char * U_EXPORT2
+ucasemap_getLocale(const UCaseMap *csm);
+
+/**
+ * Get the options bit set that is used for case folding and string comparisons.
+ * @param csm UCaseMap service object.
+ * @return options bit set
+ * @draft ICU 3.4
+ */
+U_DRAFT uint32_t U_EXPORT2
+ucasemap_getOptions(const UCaseMap *csm);
+
+/**
+ * Set the locale ID that is used for language-dependent case mappings.
+ *
+ * @param csm UCaseMap service object.
+ * @param locale Locale ID, see ucasemap_open().
+ * @param pErrorCode Must be a valid pointer to an error code value,
+ * which must not indicate a failure before the function call.
+ *
+ * @see ucasemap_open
+ * @draft ICU 3.4
+ */
+U_DRAFT void U_EXPORT2
+ucasemap_setLocale(UCaseMap *csm, const char *locale, UErrorCode *pErrorCode);
+
+/**
+ * Set the options bit set that is used for case folding and string comparisons.
+ *
+ * @param csm UCaseMap service object.
+ * @param options Options bit set, see ucasemap_open().
+ * @param pErrorCode Must be a valid pointer to an error code value,
+ * which must not indicate a failure before the function call.
+ *
+ * @see ucasemap_open
+ * @draft ICU 3.4
+ */
+U_DRAFT void U_EXPORT2
+ucasemap_setOptions(UCaseMap *csm, uint32_t options, UErrorCode *pErrorCode);
+
+/**
+ * Lowercase the characters in a UTF-8 string.
+ * Casing is locale-dependent and context-sensitive.
+ * The result may be longer or shorter than the original.
+ * The source string and the destination buffer must not overlap.
+ *
+ * @param csm UCaseMap service object.
+ * @param dest A buffer for the result string. The result will be NUL-terminated if
+ * the buffer is large enough.
+ * The contents is undefined in case of failure.
+ * @param destCapacity The size of the buffer (number of bytes). If it is 0, then
+ * dest may be NULL and the function will only return the length of the result
+ * without writing any of the result string.
+ * @param src The original string
+ * @param srcLength The length of the original string. If -1, then src must be NUL-terminated.
+ * @param pErrorCode Must be a valid pointer to an error code value,
+ * which must not indicate a failure before the function call.
+ * @return The length of the result string, if successful - or in case of a buffer overflow,
+ * in which case it will be greater than destCapacity.
+ *
+ * @see u_strToLower
+ * @draft ICU 3.4
+ */
+U_DRAFT int32_t U_EXPORT2
+ucasemap_utf8ToLower(const UCaseMap *csm,
+ char *dest, int32_t destCapacity,
+ const char *src, int32_t srcLength,
+ UErrorCode *pErrorCode);
+
+/**
+ * Uppercase the characters in a UTF-8 string.
+ * Casing is locale-dependent and context-sensitive.
+ * The result may be longer or shorter than the original.
+ * The source string and the destination buffer must not overlap.
+ *
+ * @param csm UCaseMap service object.
+ * @param dest A buffer for the result string. The result will be NUL-terminated if
+ * the buffer is large enough.
+ * The contents is undefined in case of failure.
+ * @param destCapacity The size of the buffer (number of bytes). If it is 0, then
+ * dest may be NULL and the function will only return the length of the result
+ * without writing any of the result string.
+ * @param src The original string
+ * @param srcLength The length of the original string. If -1, then src must be NUL-terminated.
+ * @param pErrorCode Must be a valid pointer to an error code value,
+ * which must not indicate a failure before the function call.
+ * @return The length of the result string, if successful - or in case of a buffer overflow,
+ * in which case it will be greater than destCapacity.
+ *
+ * @see u_strToUpper
+ * @draft ICU 3.4
+ */
+U_DRAFT int32_t U_EXPORT2
+ucasemap_utf8ToUpper(const UCaseMap *csm,
+ char *dest, int32_t destCapacity,
+ const char *src, int32_t srcLength,
+ UErrorCode *pErrorCode);
+
+#endif
diff --git a/icu4c/source/common/ustrcase.c b/icu4c/source/common/ustrcase.c
index 6946d5e4a5a..aedae0a3538 100644
--- a/icu4c/source/common/ustrcase.c
+++ b/icu4c/source/common/ustrcase.c
@@ -1,7 +1,7 @@
/*
*******************************************************************************
*
-* Copyright (C) 2001-2004, International Business Machines
+* Copyright (C) 2001-2005, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@@ -121,7 +121,7 @@ UCaseMapFull(const UCaseProps *csp, UChar32 c,
const char *locale, int32_t *locCache);
/*
- * Lowercases [srcStart..srcLimit[ but takes
+ * Case-maps [srcStart..srcLimit[ but takes
* context [0..srcLength[ into account.
*/
static int32_t
@@ -132,7 +132,7 @@ _caseMap(const UCaseProps *csp, UCaseMapFull *map,
const char *locale, int32_t *locCache,
UErrorCode *pErrorCode) {
const UChar *s;
- UChar32 c;
+ UChar32 c, c2;
int32_t srcIndex, destIndex;
/* case mapping loop */
@@ -143,7 +143,12 @@ _caseMap(const UCaseProps *csp, UCaseMapFull *map,
U16_NEXT(src, srcIndex, srcLimit, c);
csc->cpLimit=srcIndex;
c=map(csp, c, utf16_caseContextIterator, csc, &s, locale, locCache);
- destIndex=appendResult(dest, destIndex, destCapacity, c, s);
+ if((destIndexdestCapacity) {
@@ -293,14 +298,19 @@ ustr_foldCase(const UCaseProps *csp,
int32_t srcIndex, destIndex;
const UChar *s;
- UChar32 c;
+ UChar32 c, c2;
/* case mapping loop */
srcIndex=destIndex=0;
while(srcIndexdestCapacity) {
diff --git a/icu4c/source/test/cintltst/cstrcase.c b/icu4c/source/test/cintltst/cstrcase.c
index dcc6047fcf6..57256e6c52d 100644
--- a/icu4c/source/test/cintltst/cstrcase.c
+++ b/icu4c/source/test/cintltst/cstrcase.c
@@ -16,11 +16,13 @@
* Test file for string casing C API functions.
*/
+#include
#include "unicode/utypes.h"
#include "unicode/uchar.h"
#include "unicode/ustring.h"
#include "unicode/uloc.h"
#include "unicode/ubrk.h"
+#include "unicode/ucasemap.h"
#include "cmemory.h"
#include "cintltst.h"
#include "cucdtst.h"
@@ -643,3 +645,138 @@ TestCaseCompare() {
log_err("error: u_memcasecmp(mixed, different, 5, default)=%ld instead of positive\n", result);
}
}
+
+/* test UCaseMap ------------------------------------------------------------ */
+
+/*
+ * API test for UCaseMap;
+ * test cases for actual case mappings using UCaseMap see
+ * intltest utility/UnicodeStringTest/StringCaseTest/TestCasing
+ */
+U_CFUNC void
+TestUCaseMap() {
+ static const char
+ aBc[] ={ 0x61, 0x42, 0x63, 0 },
+ abc[] ={ 0x61, 0x62, 0x63, 0 },
+ ABC[] ={ 0x41, 0x42, 0x43, 0 },
+ ABCg[]={ 0x41, 0x42, 0x43, 0x67, 0 },
+ defg[]={ 0x64, 0x65, 0x66, 0x67, 0 };
+ char utf8Out[8];
+
+ UCaseMap *csm;
+ const char *locale;
+ uint32_t options;
+ int32_t length;
+ UErrorCode errorCode;
+
+ errorCode=U_ZERO_ERROR;
+ csm=ucasemap_open("tur", 0xa5, &errorCode);
+ if(U_FAILURE(errorCode)) {
+ log_err("ucasemap_open(\"tur\") failed - %s\n", u_errorName(errorCode));
+ return;
+ }
+ locale=ucasemap_getLocale(csm);
+ if(0!=strcmp(locale, "tr")) {
+ log_err("ucasemap_getLocale(ucasemap_open(\"tur\"))==%s!=\"tr\"\n", locale);
+ }
+ /* overly long locale IDs get truncated to their language code to avoid unnecessary allocation */
+ ucasemap_setLocale(csm, "I-kLInGOn-the-quick-brown-fox-jumps-over-the-lazy-dog", &errorCode);
+ locale=ucasemap_getLocale(csm);
+ if(0!=strcmp(locale, "i-klingon")) {
+ log_err("ucasemap_getLocale(ucasemap_setLocale(\"I-kLInGOn-the-quick-br...\"))==%s!=\"i-klingon\"\n", locale);
+ }
+
+ errorCode=U_ZERO_ERROR;
+ options=ucasemap_getOptions(csm);
+ if(options!=0xa5) {
+ log_err("ucasemap_getOptions(ucasemap_open(0xa5))==0x%lx!=0xa5\n", (long)options);
+ }
+ ucasemap_setOptions(csm, 0x333333, &errorCode);
+ options=ucasemap_getOptions(csm);
+ if(options!=0x333333) {
+ log_err("ucasemap_getOptions(ucasemap_setOptions(0x333333))==0x%lx!=0x333333\n", (long)options);
+ }
+
+ /* test case mapping API; not all permutations necessary due to shared implementation code */
+
+ /* NUL terminated source */
+ errorCode=U_ZERO_ERROR;
+ length=ucasemap_utf8ToLower(csm, utf8Out, (int32_t)sizeof(utf8Out), aBc, -1, &errorCode);
+ if(U_FAILURE(errorCode) || length!=3 || 0!=strcmp(abc, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(aBc\\0) failed\n");
+ }
+
+ /* incoming failure code */
+ errorCode=U_PARSE_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToLower(csm, utf8Out, (int32_t)sizeof(utf8Out), aBc, -1, &errorCode);
+ if(errorCode!=U_PARSE_ERROR || 0!=strcmp(defg, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(failure) failed\n");
+ }
+
+ /* overlapping input & output */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, aBc);
+ length=ucasemap_utf8ToUpper(csm, utf8Out, 2, utf8Out+1, 2, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(aBc, utf8Out)) {
+ log_err("ucasemap_utf8ToUpper(overlap 1) failed\n");
+ }
+
+ /* overlap in the other direction */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, aBc);
+ length=ucasemap_utf8ToUpper(csm, utf8Out+1, 2, utf8Out, 2, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(aBc, utf8Out)) {
+ log_err("ucasemap_utf8ToUpper(overlap 2) failed\n");
+ }
+
+ /* NULL destination */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToLower(csm, NULL, (int32_t)sizeof(utf8Out), aBc, -1, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(defg, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(dest=NULL) failed\n");
+ }
+
+ /* destCapacity<0 */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToLower(csm, utf8Out, -2, aBc, -1, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(defg, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(destCapacity<0) failed\n");
+ }
+
+ /* NULL source */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToLower(csm, utf8Out, (int32_t)sizeof(utf8Out), NULL, -1, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(defg, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(src=NULL) failed\n");
+ }
+
+ /* srcLength<-1 */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToLower(csm, utf8Out, (int32_t)sizeof(utf8Out), aBc, -2, &errorCode);
+ if(errorCode!=U_ILLEGAL_ARGUMENT_ERROR || 0!=strcmp(defg, utf8Out)) {
+ log_err("ucasemap_utf8ToLower(srcLength<-1) failed\n");
+ }
+
+ /* buffer overflow */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToUpper(csm, utf8Out, 2, aBc, 3, &errorCode);
+ if(errorCode!=U_BUFFER_OVERFLOW_ERROR || length!=3 || 0!=strcmp(defg+2, utf8Out+2)) {
+ log_err("ucasemap_utf8ToUpper(overflow) failed\n");
+ }
+
+ /* dest not terminated (leaves g from defg alone) */
+ errorCode=U_ZERO_ERROR;
+ strcpy(utf8Out, defg);
+ length=ucasemap_utf8ToUpper(csm, utf8Out, 3, aBc, 3, &errorCode);
+ if(errorCode!=U_STRING_NOT_TERMINATED_WARNING || length!=3 || 0!=strcmp(ABCg, utf8Out)) {
+ log_err("ucasemap_utf8ToUpper(overflow) failed\n");
+ }
+
+ ucasemap_close(csm);
+}
diff --git a/icu4c/source/test/cintltst/cucdtst.h b/icu4c/source/test/cintltst/cucdtst.h
index 336476a0892..ef3a68c53bc 100644
--- a/icu4c/source/test/cintltst/cucdtst.h
+++ b/icu4c/source/test/cintltst/cucdtst.h
@@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2001, International Business Machines Corporation and
+ * Copyright (c) 1997-2005, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
/********************************************************************************
@@ -23,5 +23,6 @@ U_CFUNC void TestCaseUpper(void);
U_CFUNC void TestCaseTitle(void);
U_CFUNC void TestCaseFolding(void);
U_CFUNC void TestCaseCompare(void);
+U_CFUNC void TestUCaseMap(void);
#endif
diff --git a/icu4c/source/test/cintltst/custrtst.c b/icu4c/source/test/cintltst/custrtst.c
index 358dd006472..c5f1a22b182 100644
--- a/icu4c/source/test/cintltst/custrtst.c
+++ b/icu4c/source/test/cintltst/custrtst.c
@@ -1,7 +1,7 @@
/*
******************************************************************************
*
-* Copyright (C) 2002-2004, International Business Machines
+* Copyright (C) 2002-2005, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
@@ -64,6 +64,7 @@ void addUStringTest(TestNode** root)
#endif
addTest(root, &TestCaseFolding, "tsutil/custrtst/TestCaseFolding");
addTest(root, &TestCaseCompare, "tsutil/custrtst/TestCaseCompare");
+ addTest(root, &TestUCaseMap, "tsutil/custrtst/TestUCaseMap");
}
/* test data for TestStringFunctions ---------------------------------------- */
diff --git a/icu4c/source/test/intltest/strcase.cpp b/icu4c/source/test/intltest/strcase.cpp
index a2dc70d7ee6..f6d5b6102d1 100644
--- a/icu4c/source/test/intltest/strcase.cpp
+++ b/icu4c/source/test/intltest/strcase.cpp
@@ -1,7 +1,7 @@
/*
*******************************************************************************
*
-* Copyright (C) 2002-2004, International Business Machines
+* Copyright (C) 2002-2005, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@@ -21,6 +21,8 @@
#include "unicode/uloc.h"
#include "unicode/locid.h"
#include "unicode/ubrk.h"
+#include "unicode/unistr.h"
+#include "unicode/ucasemap.h"
#include "ustrtest.h"
#include "unicode/tstdtmod.h"
@@ -32,9 +34,9 @@ StringCaseTest::runIndexedTest(int32_t index, UBool exec, const char *&name, cha
switch (index) {
case 0: name = "TestCaseConversion"; if (exec) TestCaseConversion(); break;
case 1:
- name = "TestTitleCasing";
+ name = "TestCasing";
#if !UCONFIG_NO_BREAK_ITERATION
- if(exec) TestTitleCasing();
+ if(exec) TestCasing();
#endif
break;
@@ -45,21 +47,21 @@ StringCaseTest::runIndexedTest(int32_t index, UBool exec, const char *&name, cha
void
StringCaseTest::TestCaseConversion()
{
- UChar uppercaseGreek[] =
+ static const UChar uppercaseGreek[] =
{ 0x399, 0x395, 0x3a3, 0x3a5, 0x3a3, 0x20, 0x03a7, 0x3a1, 0x399, 0x3a3, 0x3a4,
0x39f, 0x3a3, 0 };
// "IESUS CHRISTOS"
- UChar lowercaseGreek[] =
+ static const UChar lowercaseGreek[] =
{ 0x3b9, 0x3b5, 0x3c3, 0x3c5, 0x3c2, 0x20, 0x03c7, 0x3c1, 0x3b9, 0x3c3, 0x3c4,
0x3bf, 0x3c2, 0 };
// "iesus christos"
- UChar lowercaseTurkish[] =
+ static const UChar lowercaseTurkish[] =
{ 0x69, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f,
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x0131, 0x6e, 0x6f, 0x70, 0x6c, 0x65, 0x21, 0 };
- UChar uppercaseTurkish[] =
+ static const UChar uppercaseTurkish[] =
{ 0x54, 0x4f, 0x50, 0x4b, 0x41, 0x50, 0x49, 0x20, 0x50, 0x41, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20,
0x0130, 0x53, 0x54, 0x41, 0x4e, 0x42, 0x55, 0x4c, 0 };
@@ -318,45 +320,176 @@ StringCaseTest::TestCaseConversion()
}
}
+// data-driven case mapping tests ------------------------------------------ ***
+
+enum {
+ TEST_LOWER,
+ TEST_UPPER,
+#if !UCONFIG_NO_BREAK_ITERATION
+ TEST_TITLE,
+#endif
+ TEST_COUNT
+};
+
+// names of TestData children in casing.txt
+static const char *const dataNames[TEST_COUNT+1]={
+ "lowercasing",
+ "uppercasing",
+#if !UCONFIG_NO_BREAK_ITERATION
+ "titlecasing",
+#endif
+ ""
+};
+
+void
+StringCaseTest::TestCasingImpl(const UnicodeString &input,
+ const UnicodeString &output,
+ int32_t whichCase,
+ const char *localeID, uint32_t options) {
+ // UnicodeString
+ UnicodeString result;
+ const char *name;
+
+ result=input;
+ switch(whichCase) {
+ case TEST_LOWER:
+ name="toLower";
+ result.toLower(Locale(localeID));
+ break;
+ case TEST_UPPER:
+ name="toUpper";
+ result.toUpper(Locale(localeID));
+ break;
+ default:
+ name="";
+ break; // won't happen
+ }
+ if(result!=output) {
+ errln("error: UnicodeString.%s() got a wrong result for a test case from casing.res", name);
+ }
+
+ // UTF-8
+ char utf8In[100], utf8Out[100];
+ int32_t utf8InLength, utf8OutLength, resultLength;
+ UChar *buffer;
+
+ UCaseMap *csm;
+ UErrorCode errorCode;
+
+ errorCode=U_ZERO_ERROR;
+ csm=ucasemap_open(localeID, options, &errorCode);
+
+ u_strToUTF8(utf8In, (int32_t)sizeof(utf8In), &utf8InLength, input.getBuffer(), input.length(), &errorCode);
+ switch(whichCase) {
+ case TEST_LOWER:
+ name="ucasemap_utf8ToLower";
+ utf8OutLength=ucasemap_utf8ToLower(csm,
+ utf8Out, (int32_t)sizeof(utf8Out),
+ utf8In, utf8InLength, &errorCode);
+ break;
+ case TEST_UPPER:
+ name="ucasemap_utf8ToUpper";
+ utf8OutLength=ucasemap_utf8ToUpper(csm,
+ utf8Out, (int32_t)sizeof(utf8Out),
+ utf8In, utf8InLength, &errorCode);
+ break;
+ default:
+ name="";
+ utf8OutLength=0;
+ break; // won't happen
+ }
+ buffer=result.getBuffer(utf8OutLength);
+ u_strFromUTF8(buffer, result.getCapacity(), &resultLength, utf8Out, utf8OutLength, &errorCode);
+ result.releaseBuffer(U_SUCCESS(errorCode) ? resultLength : 0);
+
+ if(U_FAILURE(errorCode)) {
+ errln("error: %s() got an error for a test case from casing.res - %s", name, u_errorName(errorCode));
+ } else if(result!=output) {
+ errln("error: %s() got a wrong result for a test case from casing.res", name);
+ }
+ ucasemap_close(csm);
+}
+
#if !UCONFIG_NO_BREAK_ITERATION
void
-StringCaseTest::TestTitleCasing() {
+StringCaseTest::TestTitleCasing(const UnicodeString &input,
+ const UnicodeString &output,
+ const char *localeID,
+ UBreakIterator *iter) {
+ UnicodeString result;
+
+ result=input;
+ result.toTitle((BreakIterator *)iter, Locale(localeID));
+ if(result!=output) {
+ errln("error: UnicodeString.toTitle() got a wrong result for a test case from casing.res");
+ }
+}
+
+#endif
+
+void
+StringCaseTest::TestCasing() {
UErrorCode status = U_ZERO_ERROR;
+#if !UCONFIG_NO_BREAK_ITERATION
UBreakIterator *iter;
+#endif
char cLocaleID[100];
- UnicodeString locale, input, result;
- int32_t type;
+ UnicodeString locale, input, output, result;
+ int32_t whichCase, type;
TestDataModule *driver = TestDataModule::getTestDataModule("casing", *this, status);
if(U_SUCCESS(status)) {
- TestData *casingTest = driver->createTestData("titlecasing", status);
- const DataMap *myCase = NULL;
- while(casingTest->nextCase(myCase, status)) {
- locale = myCase->getString("Locale", status);
- locale.extract(0, 0x7fffffff, cLocaleID, sizeof(cLocaleID), "");
- type = myCase->getInt("Type", status);
-
-
- input = myCase->getString("Input", status);
- if(type<0) {
- iter=0;
- } else {
- iter=ubrk_open((UBreakIteratorType)type, cLocaleID, NULL, 0, &status);
- }
-
+ for(whichCase=0; whichCasecreateTestData(dataNames[whichCase], status);
if(U_FAILURE(status)) {
- errln("error: TestTitleCasing() ubrk_open(%d) failed for test case from casing.res: %s", type, u_errorName(status));
- status = U_ZERO_ERROR;
- } else {
- result=input;
- result.toTitle((BreakIterator *)iter, Locale(cLocaleID));
- if(result!=myCase->getString("Output", status)) {
- errln("error: TestTitleCasing() got a wrong result for test case from casing.res");
- }
- ubrk_close(iter);
+ errln("TestCasing failed to createTestData(%s) - %s", dataNames[whichCase], u_errorName(status));
+ break;
}
+ const DataMap *myCase = NULL;
+ while(casingTest->nextCase(myCase, status)) {
+ locale = myCase->getString("Locale", status);
+ locale.extract(0, 0x7fffffff, cLocaleID, sizeof(cLocaleID), "");
+
+ input = myCase->getString("Input", status);
+ output = myCase->getString("Output", status);
+
+#if !UCONFIG_NO_BREAK_ITERATION
+ iter=NULL;
+ if(whichCase==TEST_TITLE) {
+ type = myCase->getInt("Type", status);
+ if(type>=0) {
+ iter=ubrk_open((UBreakIteratorType)type, cLocaleID, NULL, 0, &status);
+ }
+ }
+#endif
+
+ if(U_FAILURE(status)) {
+ errln("error: TestCasing() setup failed for %s test case from casing.res: %s", dataNames[whichCase], u_errorName(status));
+ status = U_ZERO_ERROR;
+ } else {
+ switch(whichCase) {
+ case TEST_LOWER:
+ case TEST_UPPER:
+ TestCasingImpl(input, output, whichCase, cLocaleID, 0);
+ break;
+#if !UCONFIG_NO_BREAK_ITERATION
+ case TEST_TITLE:
+ TestTitleCasing(input, output, cLocaleID, iter);
+ break;
+#endif
+ default:
+ break; // won't happen
+ }
+ }
+
+#if !UCONFIG_NO_BREAK_ITERATION
+ if(iter!=NULL) {
+ ubrk_close(iter);
+ }
+#endif
+ }
+ delete casingTest;
}
- delete casingTest;
}
delete driver;
@@ -367,77 +500,4 @@ StringCaseTest::TestTitleCasing() {
if(result!=UNICODE_STRING_SIMPLE("Stra\\u00dfe").unescape()) {
errln("UnicodeString::toTitle(NULL) failed");
}
-
-#if 0
- char cLocaleID[100];
- UnicodeString in, expect, result, localeID;
- UResourceBundle *casing, *titlecasing, *test, *res;
- UErrorCode errorCode;
- int32_t testIndex, type;
-
- errorCode=U_ZERO_ERROR;
- loadTestData(errorCode);
- casing=ures_openDirect("testdata", "casing", &errorCode);
- if(U_FAILURE(errorCode)) {
- errln("error: TestTitleCasing() is unable to open casing.res: %s", u_errorName(errorCode));
- return;
- }
-
- // titlecasing tests
- titlecasing=ures_getByKey(casing, "titlecasing", 0, &errorCode);
- if(U_FAILURE(errorCode)) {
- logln("TestTitleCasing() is unable to open get casing.res/titlecasing: %s", u_errorName(errorCode));
- } else {
- UBreakIterator *iter;
-
- for(testIndex=0;; ++testIndex) {
- // get test case
- test=ures_getByIndex(titlecasing, testIndex, 0, &errorCode);
- if(U_FAILURE(errorCode)) {
- break; // done
- }
-
- // get test case data
- in=ures_getUnicodeStringByIndex(test, 0, &errorCode);
- expect=ures_getUnicodeStringByIndex(test, 1, &errorCode);
- localeID=ures_getUnicodeStringByIndex(test, 2, &errorCode);
-
- res=ures_getByIndex(test, 3, 0, &errorCode);
- type=ures_getInt(res, &errorCode);
- ures_close(res);
-
- if(U_FAILURE(errorCode)) {
- errln("error: TestTitleCasing() is unable to get data for test case %ld from casing.res: %s", testIndex, u_errorName(errorCode));
- continue; // skip this test case
- }
-
- // run this test case
- localeID.extract(0, 0x7fffffff, cLocaleID, sizeof(cLocaleID), "");
- if(type<0) {
- iter=0;
- } else {
- iter=ubrk_open((UBreakIteratorType)type, cLocaleID, in.getBuffer(), in.length(), &errorCode);
- }
-
- if(U_FAILURE(errorCode)) {
- errln("error: TestTitleCasing() ubrk_open(%d) failed for test case %d from casing.res: %s", type, testIndex, u_errorName(errorCode));
- } else {
- result=in;
- result.toTitle((BreakIterator *)iter, Locale(cLocaleID));
- if(result!=expect) {
- errln("error: TestTitleCasing() got a wrong result for test case %ld from casing.res", testIndex);
- }
- }
-
- // clean up
- ubrk_close(iter);
- ures_close(test);
- }
- ures_close(titlecasing);
- logln("TestTitleCasing() processed %ld test cases", testIndex);
- }
-
- ures_close(casing);
-#endif
}
-#endif
diff --git a/icu4c/source/test/intltest/ustrtest.h b/icu4c/source/test/intltest/ustrtest.h
index 8fc751ec4a1..c3f9f39947e 100644
--- a/icu4c/source/test/intltest/ustrtest.h
+++ b/icu4c/source/test/intltest/ustrtest.h
@@ -1,12 +1,14 @@
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2003, International Business Machines Corporation and
+ * Copyright (c) 1997-2005, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
#ifndef UNICODESTRINGTEST_H
#define UNICODESTRINGTEST_H
+#include "unicode/unistr.h"
+#include "unicode/ubrk.h"
#include "intltest.h"
/**
@@ -83,9 +85,18 @@ public:
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
void TestCaseConversion();
+
+ void TestCasingImpl(const UnicodeString &input,
+ const UnicodeString &output,
+ int32_t whichCase,
+ const char *localeID, uint32_t options);
#if !UCONFIG_NO_BREAK_ITERATION
- void TestTitleCasing();
+ void TestTitleCasing(const UnicodeString &input,
+ const UnicodeString &output,
+ const char *localeID,
+ UBreakIterator *iter);
#endif
+ void TestCasing();
};
#endif
diff --git a/icu4c/source/test/testdata/casing.txt b/icu4c/source/test/testdata/casing.txt
index c4c3bb144be..1e1c95c23e6 100644
--- a/icu4c/source/test/testdata/casing.txt
+++ b/icu4c/source/test/testdata/casing.txt
@@ -1,21 +1,38 @@
//*******************************************************************************
//*
-//* Copyright (C) 2002, International Business Machines
+//* Copyright (C) 2002-2005, International Business Machines
//* Corporation and others. All Rights Reserved.
//*
//*******************************************************************************
casing {
Info {
- Description { "This is test data file for string casing" }
+ Description { "This is test data file for string casing." }
- LongDescription { "each item is an array with"
- "input string, result string, locale ID, break iterator"
- "the break iterator is specified as an int, same as in UBreakIteratorType:"
- "0=UBRK_CHARACTER 1=UBRK_WORD 2=UBRK_LINE 3=UBRK_SENTENCE 4=UBRK_TITLE -1=default"
- }
+ LongDescription {
+ "each item is an array with\n"
+ "input string, result string, locale ID[, break iterator]\n"
+ "the break iterator (only for titlecasing) is specified as an int, same as in UBreakIteratorType:\n"
+ "0=UBRK_CHARACTER 1=UBRK_WORD 2=UBRK_LINE 3=UBRK_SENTENCE 4=UBRK_TITLE -1=default\n"
+ }
}
TestData {
+ lowercasing {
+ Headers { "Input", "Output", "Locale" }
+ Cases {
+ { " tHe QUIcK bRoWn", " the quick brown", "" },
+ { "aBIΣßΣ/𐐅", "abiσßς/𐐭", "" },
+ { "aBIΣßΣ/𐐅", "abıσßς/𐐭", "tur" } // tur: 3-letter code for Turkish
+ }
+ }
+ uppercasing {
+ Headers { "Input", "Output", "Locale" }
+ Cases {
+ { " tHe QUIcK bRoWn", " THE QUICK BROWN", "" },
+ { "aBiσßς/ffi𐐭", "ABIΣSSΣ/FFI𐐅", "" },
+ { "aBiσßς/ffi𐐭", "ABİΣSSΣ/FFI𐐅", "az" } // az same casing as tr
+ }
+ }
titlecasing {
Headers { "Input", "Output", "Locale", "Type" }
Cases {