2 *****************************************************************
3 * Copyright (c) 2002-2004, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 *****************************************************************
6 * Date Name Description
7 * 06/06/2002 aliu Creation.
8 *****************************************************************
11 #include "unicode/utypes.h"
13 #if !UCONFIG_NO_TRANSLITERATION
15 #include "unicode/uobject.h"
16 #include "unicode/uscript.h"
24 //------------------------------------------------------------
27 static const UChar TARGET_SEP
= 45; // '-'
28 static const UChar VARIANT_SEP
= 47; // '/'
29 static const UChar ANY
[] = {65,110,121,0}; // "Any"
30 static const UChar NULL_ID
[] = {78,117,108,108,0}; // "Null"
31 static const UChar LATIN_PIVOT
[] = {45,76,97,116,105,110,59,76,97,116,105,110,45,0}; // "-Latin;Latin-"
33 //------------------------------------------------------------
37 * Deleter function for Transliterator*.
39 static void U_CALLCONV
40 _deleteTransliterator(void *obj
) {
41 delete (Transliterator
*) obj
;
45 //------------------------------------------------------------
49 //------------------------------------------------------------
53 * Returns a series of ranges corresponding to scripts. They will be
56 * ccccSScSSccccTTcTcccc - c = common, S = first script, T = second
57 * | | - first run (start, limit)
58 * | | - second run (start, limit)
60 * That is, the runs will overlap. The reason for this is so that a
61 * transliterator can consider common characters both before and after
64 class ScriptRunIterator
: public UMemory
{
66 const Replaceable
& text
;
72 * The code of the current run, valid after next() returns. May
73 * be USCRIPT_INVALID_CODE if and only if the entire text is
76 UScriptCode scriptCode
;
79 * The start of the run, inclusive, valid after next() returns.
84 * The end of the run, exclusive, valid after next() returns.
89 * Constructs a run iterator over the given text from start
90 * (inclusive) to limit (exclusive).
92 ScriptRunIterator(const Replaceable
& text
, int32_t start
, int32_t limit
);
95 * Returns TRUE if there are any more runs. TRUE is always
96 * returned at least once. Upon return, the caller should
97 * examine scriptCode, start, and limit.
102 * Adjusts internal indices for a change in the limit index of the
103 * given delta. A positive delta means the limit has increased.
105 void adjustLimit(int32_t delta
);
108 ScriptRunIterator(const ScriptRunIterator
&other
); // forbid copying of this class
109 ScriptRunIterator
&operator=(const ScriptRunIterator
&other
); // forbid copying of this class
112 ScriptRunIterator::ScriptRunIterator(const Replaceable
& theText
,
113 int32_t myStart
, int32_t myLimit
) :
121 UBool
ScriptRunIterator::next() {
124 UErrorCode ec
= U_ZERO_ERROR
;
126 scriptCode
= USCRIPT_INVALID_CODE
; // don't know script yet
130 if (start
== textLimit
) {
134 // Move start back to include adjacent COMMON or INHERITED
136 while (start
> textStart
) {
137 ch
= text
.char32At(start
- 1); // look back
138 s
= uscript_getScript(ch
, &ec
);
139 if (s
== USCRIPT_COMMON
|| s
== USCRIPT_INHERITED
) {
146 // Move limit ahead to include COMMON, INHERITED, and characters
147 // of the current script.
148 while (limit
< textLimit
) {
149 ch
= text
.char32At(limit
); // look ahead
150 s
= uscript_getScript(ch
, &ec
);
151 if (s
!= USCRIPT_COMMON
&& s
!= USCRIPT_INHERITED
) {
152 if (scriptCode
== USCRIPT_INVALID_CODE
) {
154 } else if (s
!= scriptCode
) {
161 // Return TRUE even if the entire text is COMMON / INHERITED, in
162 // which case scriptCode will be USCRIPT_INVALID_CODE.
166 void ScriptRunIterator::adjustLimit(int32_t delta
) {
171 //------------------------------------------------------------
174 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnyTransliterator
)
176 AnyTransliterator::AnyTransliterator(const UnicodeString
& id
,
177 const UnicodeString
& theTarget
,
178 const UnicodeString
& theVariant
,
179 UScriptCode theTargetScript
,
181 Transliterator(id
, NULL
),
182 targetScript(theTargetScript
)
184 cache
= uhash_open(uhash_hashLong
, uhash_compareLong
, &ec
);
185 uhash_setValueDeleter(cache
, _deleteTransliterator
);
188 if (theVariant
.length() > 0) {
189 target
.append(VARIANT_SEP
).append(theVariant
);
193 AnyTransliterator::~AnyTransliterator() {
200 AnyTransliterator::AnyTransliterator(const AnyTransliterator
& o
) :
203 targetScript(o
.targetScript
)
205 // Don't copy the cache contents
206 UErrorCode ec
= U_ZERO_ERROR
;
207 cache
= uhash_open(uhash_hashLong
, uhash_compareLong
, &ec
);
208 uhash_setValueDeleter(cache
, _deleteTransliterator
);
212 * Transliterator API.
214 Transliterator
* AnyTransliterator::clone() const {
215 return new AnyTransliterator(*this);
219 * Implements {@link Transliterator#handleTransliterate}.
221 void AnyTransliterator::handleTransliterate(Replaceable
& text
, UTransPosition
& pos
,
222 UBool isIncremental
) const {
223 int32_t allStart
= pos
.start
;
224 int32_t allLimit
= pos
.limit
;
226 ScriptRunIterator
it(text
, pos
.contextStart
, pos
.contextLimit
);
229 // Ignore runs in the ante context
230 if (it
.limit
<= allStart
) continue;
232 // Try to instantiate transliterator from it.scriptCode to
233 // our target or target/variant
234 Transliterator
* t
= getTransliterator(it
.scriptCode
);
237 // We have no transliterator. Do nothing, but keep
238 // pos.start up to date.
239 pos
.start
= it
.limit
;
243 // If the run end is before the transliteration limit, do
244 // a non-incremental transliteration. Otherwise do an
246 UBool incremental
= isIncremental
&& (it
.limit
>= allLimit
);
248 pos
.start
= uprv_max(allStart
, it
.start
);
249 pos
.limit
= uprv_min(allLimit
, it
.limit
);
250 int32_t limit
= pos
.limit
;
251 t
->filteredTransliterate(text
, pos
, incremental
);
252 int32_t delta
= pos
.limit
- limit
;
254 it
.adjustLimit(delta
);
256 // We're done if we enter the post context
257 if (it
.limit
>= allLimit
) break;
260 // Restore limit. pos.start is fine where the last transliterator
261 // left it, or at the end of the last run.
262 pos
.limit
= allLimit
;
265 Transliterator
* AnyTransliterator::getTransliterator(UScriptCode source
) const {
267 if (source
== targetScript
|| source
== USCRIPT_INVALID_CODE
) {
271 Transliterator
* t
= (Transliterator
*) uhash_iget(cache
, (int32_t) source
);
273 UErrorCode ec
= U_ZERO_ERROR
;
274 UnicodeString
sourceName(uscript_getName(source
), "");
275 UnicodeString
id(sourceName
);
276 id
.append(TARGET_SEP
).append(target
);
278 t
= Transliterator::createInstance(id
, UTRANS_FORWARD
, ec
);
279 if (U_FAILURE(ec
) || t
== NULL
) {
282 // Try to pivot around Latin, our most common script
284 id
.append(LATIN_PIVOT
).append(target
);
285 t
= Transliterator::createInstance(id
, UTRANS_FORWARD
, ec
);
286 if (U_FAILURE(ec
) || t
== NULL
) {
293 uhash_iput(cache
, (int32_t) source
, t
, &ec
);
301 * Return the script code for a given name, or -1 if not found.
303 UScriptCode
AnyTransliterator::scriptNameToCode(const UnicodeString
& name
) {
306 UErrorCode ec
= U_ZERO_ERROR
;
308 name
.extract(0, 128, buf
, 128, "");
309 if (uscript_getCode(buf
, &code
, 1, &ec
) != 1 ||
311 code
= USCRIPT_INVALID_CODE
;
317 * Registers standard transliterators with the system. Called by
318 * Transliterator during initialization. Scan all current targets and
319 * register those that are scripts T as Any-T/V.
321 void AnyTransliterator::registerIDs() {
323 UErrorCode ec
= U_ZERO_ERROR
;
324 Hashtable
seen(TRUE
, ec
);
326 int32_t sourceCount
= Transliterator::_countAvailableSources();
327 for (int32_t s
=0; s
<sourceCount
; ++s
) {
328 UnicodeString source
;
329 Transliterator::_getAvailableSource(s
, source
);
331 // Ignore the "Any" source
332 if (source
.caseCompare(ANY
, 0 /*U_FOLD_CASE_DEFAULT*/) == 0) continue;
334 int32_t targetCount
= Transliterator::_countAvailableTargets(source
);
335 for (int32_t t
=0; t
<targetCount
; ++t
) {
336 UnicodeString target
;
337 Transliterator::_getAvailableTarget(t
, source
, target
);
339 // Only process each target once
340 if (seen
.geti(target
) != 0) continue;
342 seen
.puti(target
, 1, ec
);
344 // Get the script code for the target. If not a script, ignore.
345 UScriptCode targetScript
= scriptNameToCode(target
);
346 if (targetScript
== USCRIPT_INVALID_CODE
) continue;
348 int32_t variantCount
= Transliterator::_countAvailableVariants(source
, target
);
349 // assert(variantCount >= 1);
350 for (int32_t v
=0; v
<variantCount
; ++v
) {
351 UnicodeString variant
;
352 Transliterator::_getAvailableVariant(v
, source
, target
, variant
);
355 TransliteratorIDParser::STVtoID(ANY
, target
, variant
, id
);
357 AnyTransliterator
* t
= new AnyTransliterator(id
, target
, variant
,
362 Transliterator::_registerInstance(t
);
363 Transliterator::_registerSpecialInverse(target
, NULL_ID
, FALSE
);
372 #endif /* #if !UCONFIG_NO_TRANSLITERATION */