/// <summary> /// Implements <see cref="Transliterator.HandleTransliterate(IReplaceable, Position, bool)"/>. /// </summary> protected override void HandleTransliterate(IReplaceable text, Position offsets, bool isIncremental) { lock (this) { if (csp == null) { return; } if (offsets.Start >= offsets.Limit) { return; } iter.SetText(text); result.Length = 0; int c, delta; // Walk through original string // If there is a case change, modify corresponding position in replaceable iter.SetIndex(offsets.Start); iter.SetLimit(offsets.Limit); iter.SetContextLimits(offsets.ContextStart, offsets.ContextLimit); while ((c = iter.NextCaseMapCP()) >= 0) { c = csp.ToFullLower(c, iter, result, caseLocale); if (iter.DidReachLimit && isIncremental) { // the case mapping function tried to look beyond the context limit // wait for more input offsets.Start = iter.CaseMapCPStart; return; } /* decode the result */ if (c < 0) { /* c mapped to itself, no change */ continue; } else if (c <= UCaseProps.MAX_STRING_LENGTH) { /* replace by the mapping string */ delta = iter.Replace(result.ToString()); result.Length = 0; } else { /* replace by single-code point mapping */ delta = iter.Replace(UTF16.ValueOf(c)); } if (delta != 0) { offsets.Limit += delta; offsets.ContextLimit += delta; } } offsets.Start = offsets.Limit; } }
/// <summary> /// Implements <see cref="Transliterator.HandleTransliterate(IReplaceable, Position, bool)"/>. /// </summary> protected override void HandleTransliterate(IReplaceable text, Position offsets, bool isIncremental) { lock (this) { // TODO reimplement, see ustrcase.c // using a real word break iterator // instead of just looking for a transition between cased and uncased characters // call CaseMapTransliterator::handleTransliterate() for lowercasing? (set fMap) // needs to take isIncremental into account because case mappings are context-sensitive // also detect when lowercasing function did not finish because of context if (offsets.Start >= offsets.Limit) { return; } // case type: >0 cased (UCaseProps.LOWER etc.) ==0 uncased <0 case-ignorable int type; // Our mode; we are either converting letter toTitle or // toLower. bool doTitle = true; // Determine if there is a preceding context of cased case-ignorable*, // in which case we want to start in toLower mode. If the // prior context is anything else (including empty) then start // in toTitle mode. int c, start; for (start = offsets.Start - 1; start >= offsets.ContextStart; start -= UTF16.GetCharCount(c)) { c = text.Char32At(start); type = csp.GetTypeOrIgnorable(c); if (type > 0) { // cased doTitle = false; break; } else if (type == 0) { // uncased but not ignorable break; } // else (type<0) case-ignorable: continue } // Convert things after a cased character toLower; things // after a uncased, non-case-ignorable character toTitle. Case-ignorable // characters are copied directly and do not change the mode. iter.SetText(text); iter.SetIndex(offsets.Start); iter.SetLimit(offsets.Limit); iter.SetContextLimits(offsets.ContextStart, offsets.ContextLimit); result.Length = 0; // Walk through original string // If there is a case change, modify corresponding position in replaceable int delta; while ((c = iter.NextCaseMapCP()) >= 0) { type = csp.GetTypeOrIgnorable(c); if (type >= 0) { // not case-ignorable if (doTitle) { c = csp.ToFullTitle(c, iter, result, caseLocale); } else { c = csp.ToFullLower(c, iter, result, caseLocale); } doTitle = type == 0; // doTitle=isUncased if (iter.DidReachLimit && isIncremental) { // the case mapping function tried to look beyond the context limit // wait for more input offsets.Start = iter.CaseMapCPStart; return; } /* decode the result */ if (c < 0) { /* c mapped to itself, no change */ continue; } else if (c <= UCaseProps.MAX_STRING_LENGTH) { /* replace by the mapping string */ delta = iter.Replace(result.ToString()); result.Length = 0; } else { /* replace by single-code point mapping */ delta = iter.Replace(UTF16.ValueOf(c)); } if (delta != 0) { offsets.Limit += delta; offsets.ContextLimit += delta; } } } offsets.Start = offsets.Limit; } }