michael@0: /* michael@0: ********************************************************************** michael@0: * Copyright (C) 2001-2011, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: ********************************************************************** michael@0: * Date Name Description michael@0: * 05/24/01 aliu Creation. michael@0: ********************************************************************** michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_TRANSLITERATION michael@0: michael@0: #include "unicode/uchar.h" michael@0: #include "unicode/uniset.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/utf16.h" michael@0: #include "titletrn.h" michael@0: #include "umutex.h" michael@0: #include "ucase.h" michael@0: #include "cpputils.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TitlecaseTransliterator) michael@0: michael@0: TitlecaseTransliterator::TitlecaseTransliterator() : michael@0: CaseMapTransliterator(UNICODE_STRING("Any-Title", 9), NULL) michael@0: { michael@0: // Need to look back 2 characters in the case of "can't" michael@0: setMaximumContextLength(2); michael@0: } michael@0: michael@0: /** michael@0: * Destructor. michael@0: */ michael@0: TitlecaseTransliterator::~TitlecaseTransliterator() { michael@0: } michael@0: michael@0: /** michael@0: * Copy constructor. michael@0: */ michael@0: TitlecaseTransliterator::TitlecaseTransliterator(const TitlecaseTransliterator& o) : michael@0: CaseMapTransliterator(o) michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * Assignment operator. michael@0: */ michael@0: /*TitlecaseTransliterator& TitlecaseTransliterator::operator=( michael@0: const TitlecaseTransliterator& o) { michael@0: CaseMapTransliterator::operator=(o); michael@0: return *this; michael@0: }*/ michael@0: michael@0: /** michael@0: * Transliterator API. michael@0: */ michael@0: Transliterator* TitlecaseTransliterator::clone(void) const { michael@0: return new TitlecaseTransliterator(*this); michael@0: } michael@0: michael@0: /** michael@0: * Implements {@link Transliterator#handleTransliterate}. michael@0: */ michael@0: void TitlecaseTransliterator::handleTransliterate( michael@0: Replaceable& text, UTransPosition& offsets, michael@0: UBool isIncremental) const michael@0: { michael@0: // TODO reimplement, see ustrcase.c michael@0: // using a real word break iterator michael@0: // instead of just looking for a transition between cased and uncased characters michael@0: // call CaseMapTransliterator::handleTransliterate() for lowercasing? (set fMap) michael@0: // needs to take isIncremental into account because case mappings are context-sensitive michael@0: // also detect when lowercasing function did not finish because of context michael@0: michael@0: if (offsets.start >= offsets.limit) { michael@0: return; michael@0: } michael@0: michael@0: // case type: >0 cased (UCASE_LOWER etc.) ==0 uncased <0 case-ignorable michael@0: int32_t type; michael@0: michael@0: // Our mode; we are either converting letter toTitle or michael@0: // toLower. michael@0: UBool doTitle = TRUE; michael@0: michael@0: // Determine if there is a preceding context of cased case-ignorable*, michael@0: // in which case we want to start in toLower mode. If the michael@0: // prior context is anything else (including empty) then start michael@0: // in toTitle mode. michael@0: UChar32 c; michael@0: int32_t start; michael@0: for (start = offsets.start - 1; start >= offsets.contextStart; start -= U16_LENGTH(c)) { michael@0: c = text.char32At(start); michael@0: type=ucase_getTypeOrIgnorable(fCsp, c); michael@0: if(type>0) { // cased michael@0: doTitle=FALSE; michael@0: break; michael@0: } else if(type==0) { // uncased but not ignorable michael@0: break; michael@0: } michael@0: // else (type<0) case-ignorable: continue michael@0: } michael@0: michael@0: // Convert things after a cased character toLower; things michael@0: // after an uncased, non-case-ignorable character toTitle. Case-ignorable michael@0: // characters are copied directly and do not change the mode. michael@0: UCaseContext csc; michael@0: uprv_memset(&csc, 0, sizeof(csc)); michael@0: csc.p = &text; michael@0: csc.start = offsets.contextStart; michael@0: csc.limit = offsets.contextLimit; michael@0: michael@0: UnicodeString tmp; michael@0: const UChar *s; michael@0: int32_t textPos, delta, result, locCache=0; michael@0: michael@0: for(textPos=offsets.start; textPos=0) { // not case-ignorable michael@0: if(doTitle) { michael@0: result=ucase_toFullTitle(fCsp, c, utrans_rep_caseContextIterator, &csc, &s, "", &locCache); michael@0: } else { michael@0: result=ucase_toFullLower(fCsp, c, utrans_rep_caseContextIterator, &csc, &s, "", &locCache); michael@0: } michael@0: doTitle = (UBool)(type==0); // doTitle=isUncased michael@0: michael@0: if(csc.b1 && isIncremental) { michael@0: // fMap() tried to look beyond the context limit michael@0: // wait for more input michael@0: offsets.start=csc.cpStart; michael@0: return; michael@0: } michael@0: michael@0: if(result>=0) { michael@0: // replace the current code point with its full case mapping result michael@0: // see UCASE_MAX_STRING_LENGTH michael@0: if(result<=UCASE_MAX_STRING_LENGTH) { michael@0: // string s[result] michael@0: tmp.setTo(FALSE, s, result); michael@0: delta=result-U16_LENGTH(c); michael@0: } else { michael@0: // single code point michael@0: tmp.setTo(result); michael@0: delta=tmp.length()-U16_LENGTH(c); michael@0: } michael@0: text.handleReplaceBetween(csc.cpStart, textPos, tmp); michael@0: if(delta!=0) { michael@0: textPos+=delta; michael@0: csc.limit=offsets.contextLimit+=delta; michael@0: offsets.limit+=delta; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: offsets.start=textPos; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_TRANSLITERATION */