U_CFUNC int32_t U_EXPORT2 ucase_hasBinaryProperty(UChar32 c, UProperty which) { /* case mapping properties */ const UChar *resultString; int32_t locCache; const UCaseProps *csp=GET_CASE_PROPS(); if(csp==NULL) { return FALSE; } switch(which) { case UCHAR_LOWERCASE: return (UBool)(UCASE_LOWER==ucase_getType(csp, c)); case UCHAR_UPPERCASE: return (UBool)(UCASE_UPPER==ucase_getType(csp, c)); case UCHAR_SOFT_DOTTED: return ucase_isSoftDotted(csp, c); case UCHAR_CASE_SENSITIVE: return ucase_isCaseSensitive(csp, c); case UCHAR_CASED: return (UBool)(UCASE_NONE!=ucase_getType(csp, c)); case UCHAR_CASE_IGNORABLE: return (UBool)(ucase_getTypeOrIgnorable(csp, c)>>2); /* * Note: The following Changes_When_Xyz are defined as testing whether * the NFD form of the input changes when Xyz-case-mapped. * However, this simpler implementation of these properties, * ignoring NFD, passes the tests. * The implementation needs to be changed if the tests start failing. * When that happens, optimizations should be used to work with the * per-single-code point ucase_toFullXyz() functions unless * the NFD form has more than one code point, * and the property starts set needs to be the union of the * start sets for normalization and case mappings. */ case UCHAR_CHANGES_WHEN_LOWERCASED: locCache=UCASE_LOC_ROOT; return (UBool)(ucase_toFullLower(csp, c, NULL, NULL, &resultString, "", &locCache)>=0); case UCHAR_CHANGES_WHEN_UPPERCASED: locCache=UCASE_LOC_ROOT; return (UBool)(ucase_toFullUpper(csp, c, NULL, NULL, &resultString, "", &locCache)>=0); case UCHAR_CHANGES_WHEN_TITLECASED: locCache=UCASE_LOC_ROOT; return (UBool)(ucase_toFullTitle(csp, c, NULL, NULL, &resultString, "", &locCache)>=0); /* case UCHAR_CHANGES_WHEN_CASEFOLDED: -- in uprops.c */ case UCHAR_CHANGES_WHEN_CASEMAPPED: locCache=UCASE_LOC_ROOT; return (UBool)( ucase_toFullLower(csp, c, NULL, NULL, &resultString, "", &locCache)>=0 || ucase_toFullUpper(csp, c, NULL, NULL, &resultString, "", &locCache)>=0 || ucase_toFullTitle(csp, c, NULL, NULL, &resultString, "", &locCache)>=0); default: return FALSE; } }
/* * Internal titlecasing function. */ static int32_t _toTitle(UCaseMap *csm, UChar *dest, int32_t destCapacity, const UChar *src, UCaseContext *csc, int32_t srcLength, UErrorCode *pErrorCode) { const UChar *s; UChar32 c; int32_t prev, titleStart, titleLimit, idx, destIndex, length; UBool isFirstIndex; if(csm->iter!=NULL) { ubrk_setText(csm->iter, src, srcLength, pErrorCode); } else { csm->iter=ubrk_open(UBRK_WORD, csm->locale, src, srcLength, pErrorCode); } if(U_FAILURE(*pErrorCode)) { return 0; } /* set up local variables */ destIndex=0; prev=0; isFirstIndex=TRUE; /* titlecasing loop */ while(prev<srcLength) { /* find next index where to titlecase */ if(isFirstIndex) { isFirstIndex=FALSE; idx=ubrk_first(csm->iter); } else { idx=ubrk_next(csm->iter); } if(idx==UBRK_DONE || idx>srcLength) { idx=srcLength; } /* * Unicode 4 & 5 section 3.13 Default Case Operations: * * R3 toTitlecase(X): Find the word boundaries based on Unicode Standard Annex * #29, "Text Boundaries." Between each pair of word boundaries, find the first * cased character F. If F exists, map F to default_title(F); then map each * subsequent character C to default_lower(C). * * In this implementation, segment [prev..index[ into 3 parts: * a) uncased characters (copy as-is) [prev..titleStart[ * b) first case letter (titlecase) [titleStart..titleLimit[ * c) subsequent characters (lowercase) [titleLimit..index[ */ if(prev<idx) { /* find and copy uncased characters [prev..titleStart[ */ titleStart=titleLimit=prev; U16_NEXT(src, titleLimit, idx, c); if((csm->options&U_TITLECASE_NO_BREAK_ADJUSTMENT)==0 && UCASE_NONE==ucase_getType(csm->csp, c)) { /* Adjust the titlecasing index (titleStart) to the next cased character. */ for(;;) { titleStart=titleLimit; if(titleLimit==idx) { /* * only uncased characters in [prev..index[ * stop with titleStart==titleLimit==index */ break; } U16_NEXT(src, titleLimit, idx, c); if(UCASE_NONE!=ucase_getType(csm->csp, c)) { break; /* cased letter at [titleStart..titleLimit[ */ } } length=titleStart-prev; if(length>0) { if((destIndex+length)<=destCapacity) { uprv_memcpy(dest+destIndex, src+prev, length*U_SIZEOF_UCHAR); } destIndex+=length; } } if(titleStart<titleLimit) { /* titlecase c which is from [titleStart..titleLimit[ */ csc->cpStart=titleStart; csc->cpLimit=titleLimit; c=ucase_toFullTitle(csm->csp, c, utf16_caseContextIterator, csc, &s, csm->locale, &csm->locCache); destIndex=appendResult(dest, destIndex, destCapacity, c, s); /* Special case Dutch IJ titlecasing */ if ( titleStart+1 < idx && ucase_getCaseLocale(csm->locale,&csm->locCache) == UCASE_LOC_DUTCH && ( src[titleStart] == (UChar32) 0x0049 || src[titleStart] == (UChar32) 0x0069 ) && ( src[titleStart+1] == (UChar32) 0x004A || src[titleStart+1] == (UChar32) 0x006A )) { c=(UChar32) 0x004A; destIndex=appendResult(dest, destIndex, destCapacity, c, s); titleLimit++; } /* lowercase [titleLimit..index[ */ if(titleLimit<idx) { if((csm->options&U_TITLECASE_NO_LOWERCASE)==0) { /* Normal operation: Lowercase the rest of the word. */ destIndex+= _caseMap( csm, ucase_toFullLower, dest+destIndex, destCapacity-destIndex, src, csc, titleLimit, idx, pErrorCode); } else { /* Optionally just copy the rest of the word unchanged. */ length=idx-titleLimit; if((destIndex+length)<=destCapacity) { uprv_memcpy(dest+destIndex, src+titleLimit, length*U_SIZEOF_UCHAR); } destIndex+=length; } } } } prev=idx; } if(destIndex>destCapacity) { *pErrorCode=U_BUFFER_OVERFLOW_ERROR; } return destIndex; }
/* * Internal titlecasing function. * * Must get titleIter!=NULL. */ static int32_t _toTitle(const UCaseProps *csp, UChar *dest, int32_t destCapacity, const UChar *src, UCaseContext *csc, int32_t srcLength, UBreakIterator *titleIter, const char *locale, int32_t *locCache, UErrorCode *pErrorCode) { const UChar *s; UChar32 c; int32_t prev, titleStart, titleLimit, index, destIndex, length; UBool isFirstIndex; /* set up local variables */ destIndex=0; prev=0; isFirstIndex=TRUE; /* titlecasing loop */ while(prev<srcLength) { /* find next index where to titlecase */ if(isFirstIndex) { isFirstIndex=FALSE; index=ubrk_first(titleIter); } else { index=ubrk_next(titleIter); } if(index==UBRK_DONE || index>srcLength) { index=srcLength; } /* * Unicode 4 & 5 section 3.13 Default Case Operations: * * R3 toTitlecase(X): Find the word boundaries based on Unicode Standard Annex * #29, "Text Boundaries." Between each pair of word boundaries, find the first * cased character F. If F exists, map F to default_title(F); then map each * subsequent character C to default_lower(C). * * In this implementation, segment [prev..index[ into 3 parts: * a) uncased characters (copy as-is) [prev..titleStart[ * b) first case letter (titlecase) [titleStart..titleLimit[ * c) subsequent characters (lowercase) [titleLimit..index[ */ if(prev<index) { /* find and copy uncased characters [prev..titleStart[ */ titleStart=titleLimit=prev; for(;;) { U16_NEXT(src, titleLimit, srcLength, c); if(UCASE_NONE!=ucase_getType(csp, c)) { break; /* cased letter at [titleStart..titleLimit[ */ } titleStart=titleLimit; if(titleLimit==index) { /* * only uncased characters in [prev..index[ * stop with titleStart==titleLimit==index */ break; } } length=titleStart-prev; if(length>0) { if((destIndex+length)<=destCapacity) { uprv_memcpy(dest+destIndex, src+prev, length*U_SIZEOF_UCHAR); } destIndex+=length; } if(titleStart<titleLimit) { /* titlecase c which is from [titleStart..titleLimit[ */ csc->cpStart=titleStart; csc->cpLimit=titleLimit; c=ucase_toFullTitle(csp, c, utf16_caseContextIterator, csc, &s, locale, locCache); destIndex=appendResult(dest, destIndex, destCapacity, c, s); /* lowercase [titleLimit..index[ */ if(titleLimit<index) { destIndex+= _caseMap( csp, ucase_toFullLower, dest+destIndex, destCapacity-destIndex, src, csc, titleLimit, index, locale, locCache, pErrorCode); } } } prev=index; } if(destIndex>destCapacity) { *pErrorCode=U_BUFFER_OVERFLOW_ERROR; } return destIndex; }
U_CFUNC int32_t U_CALLCONV ucasemap_internalUTF8ToTitle(const UCaseMap *csm, uint8_t *dest, int32_t destCapacity, const uint8_t *src, int32_t srcLength, UErrorCode *pErrorCode) { const UChar *s; UChar32 c; int32_t prev, titleStart, titleLimit, idx, destIndex, length; UBool isFirstIndex; if(U_FAILURE(*pErrorCode)) { return 0; } // Use the C++ abstract base class to minimize dependencies. // TODO: Change UCaseMap.iter to store a BreakIterator directly. BreakIterator *bi=reinterpret_cast<BreakIterator *>(csm->iter); /* set up local variables */ int32_t locCache=csm->locCache; UCaseContext csc=UCASECONTEXT_INITIALIZER; csc.p=(void *)src; csc.limit=srcLength; destIndex=0; prev=0; isFirstIndex=TRUE; /* titlecasing loop */ while(prev<srcLength) { /* find next index where to titlecase */ if(isFirstIndex) { isFirstIndex=FALSE; idx=bi->first(); } else { idx=bi->next(); } if(idx==UBRK_DONE || idx>srcLength) { idx=srcLength; } /* * Unicode 4 & 5 section 3.13 Default Case Operations: * * R3 toTitlecase(X): Find the word boundaries based on Unicode Standard Annex * #29, "Text Boundaries." Between each pair of word boundaries, find the first * cased character F. If F exists, map F to default_title(F); then map each * subsequent character C to default_lower(C). * * In this implementation, segment [prev..index[ into 3 parts: * a) uncased characters (copy as-is) [prev..titleStart[ * b) first case letter (titlecase) [titleStart..titleLimit[ * c) subsequent characters (lowercase) [titleLimit..index[ */ if(prev<idx) { /* find and copy uncased characters [prev..titleStart[ */ titleStart=titleLimit=prev; U8_NEXT(src, titleLimit, idx, c); if((csm->options&U_TITLECASE_NO_BREAK_ADJUSTMENT)==0 && UCASE_NONE==ucase_getType(csm->csp, c)) { /* Adjust the titlecasing index (titleStart) to the next cased character. */ for(;;) { titleStart=titleLimit; if(titleLimit==idx) { /* * only uncased characters in [prev..index[ * stop with titleStart==titleLimit==index */ break; } U8_NEXT(src, titleLimit, idx, c); if(UCASE_NONE!=ucase_getType(csm->csp, c)) { break; /* cased letter at [titleStart..titleLimit[ */ } } length=titleStart-prev; if(length>0) { if((destIndex+length)<=destCapacity) { uprv_memcpy(dest+destIndex, src+prev, length); } destIndex+=length; } } if(titleStart<titleLimit) { /* titlecase c which is from [titleStart..titleLimit[ */ csc.cpStart=titleStart; csc.cpLimit=titleLimit; c=ucase_toFullTitle(csm->csp, c, utf8_caseContextIterator, &csc, &s, csm->locale, &locCache); destIndex=appendResult(dest, destIndex, destCapacity, c, s); /* Special case Dutch IJ titlecasing */ if ( titleStart+1 < idx && ucase_getCaseLocale(csm->locale, &locCache) == UCASE_LOC_DUTCH && ( src[titleStart] == 0x0049 || src[titleStart] == 0x0069 ) && ( src[titleStart+1] == 0x004A || src[titleStart+1] == 0x006A )) { c=0x004A; destIndex=appendResult(dest, destIndex, destCapacity, c, s); titleLimit++; } /* lowercase [titleLimit..index[ */ if(titleLimit<idx) { if((csm->options&U_TITLECASE_NO_LOWERCASE)==0) { /* Normal operation: Lowercase the rest of the word. */ destIndex+= _caseMap( csm, ucase_toFullLower, dest+destIndex, destCapacity-destIndex, src, &csc, titleLimit, idx, pErrorCode); } else { /* Optionally just copy the rest of the word unchanged. */ length=idx-titleLimit; if((destIndex+length)<=destCapacity) { uprv_memcpy(dest+destIndex, src+titleLimit, length); } destIndex+=length; } } } } prev=idx; } if(destIndex>destCapacity) { *pErrorCode=U_BUFFER_OVERFLOW_ERROR; } return destIndex; }
U_CAPI UBool U_EXPORT2 u_isUUppercase(UChar32 c) { return (UBool)(UCASE_UPPER==ucase_getType(GET_CASE_PROPS(), c)); }
U_CFUNC void U_CALLCONV ucasemap_internalUTF8ToTitle( int32_t caseLocale, uint32_t options, BreakIterator *iter, const uint8_t *src, int32_t srcLength, ByteSink &sink, icu::Edits *edits, UErrorCode &errorCode) { if (!ustrcase_checkTitleAdjustmentOptions(options, errorCode)) { return; } /* set up local variables */ UCaseContext csc=UCASECONTEXT_INITIALIZER; csc.p=(void *)src; csc.limit=srcLength; int32_t prev=0; UBool isFirstIndex=TRUE; /* titlecasing loop */ while(prev<srcLength) { /* find next index where to titlecase */ int32_t index; if(isFirstIndex) { isFirstIndex=FALSE; index=iter->first(); } else { index=iter->next(); } if(index==UBRK_DONE || index>srcLength) { index=srcLength; } /* * Segment [prev..index[ into 3 parts: * a) skipped characters (copy as-is) [prev..titleStart[ * b) first letter (titlecase) [titleStart..titleLimit[ * c) subsequent characters (lowercase) [titleLimit..index[ */ if(prev<index) { /* find and copy skipped characters [prev..titleStart[ */ int32_t titleStart=prev; int32_t titleLimit=prev; UChar32 c; U8_NEXT(src, titleLimit, index, c); if ((options&U_TITLECASE_NO_BREAK_ADJUSTMENT)==0) { // Adjust the titlecasing index to the next cased character, // or to the next letter/number/symbol/private use. // Stop with titleStart<titleLimit<=index // if there is a character to be titlecased, // or else stop with titleStart==titleLimit==index. UBool toCased = (options&U_TITLECASE_ADJUST_TO_CASED) != 0; while (toCased ? UCASE_NONE==ucase_getType(c) : !ustrcase_isLNS(c)) { titleStart=titleLimit; if(titleLimit==index) { break; } U8_NEXT(src, titleLimit, index, c); } if (prev < titleStart) { if (!ByteSinkUtil::appendUnchanged(src+prev, titleStart-prev, sink, options, edits, errorCode)) { return; } } } if(titleStart<titleLimit) { /* titlecase c which is from [titleStart..titleLimit[ */ if(c>=0) { csc.cpStart=titleStart; csc.cpLimit=titleLimit; const UChar *s; c=ucase_toFullTitle(c, utf8_caseContextIterator, &csc, &s, caseLocale); if (!appendResult(titleLimit-titleStart, c, s, sink, options, edits, errorCode)) { return; } } else { // Malformed UTF-8. if (!ByteSinkUtil::appendUnchanged(src+titleStart, titleLimit-titleStart, sink, options, edits, errorCode)) { return; } } /* Special case Dutch IJ titlecasing */ if (titleStart+1 < index && caseLocale == UCASE_LOC_DUTCH && (src[titleStart] == 0x0049 || src[titleStart] == 0x0069)) { if (src[titleStart+1] == 0x006A) { ByteSinkUtil::appendCodePoint(1, 0x004A, sink, edits); titleLimit++; } else if (src[titleStart+1] == 0x004A) { // Keep the capital J from getting lowercased. if (!ByteSinkUtil::appendUnchanged(src+titleStart+1, 1, sink, options, edits, errorCode)) { return; } titleLimit++; } } /* lowercase [titleLimit..index[ */ if(titleLimit<index) { if((options&U_TITLECASE_NO_LOWERCASE)==0) { /* Normal operation: Lowercase the rest of the word. */ _caseMap(caseLocale, options, ucase_toFullLower, src, &csc, titleLimit, index, sink, edits, errorCode); if(U_FAILURE(errorCode)) { return; } } else { /* Optionally just copy the rest of the word unchanged. */ if (!ByteSinkUtil::appendUnchanged(src+titleLimit, index-titleLimit, sink, options, edits, errorCode)) { return; } } } } } prev=index; } }