1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/translit.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1641 @@ 1.4 +/* 1.5 + ********************************************************************** 1.6 + * Copyright (C) 1999-2012, International Business Machines 1.7 + * Corporation and others. All Rights Reserved. 1.8 + ********************************************************************** 1.9 + * Date Name Description 1.10 + * 11/17/99 aliu Creation. 1.11 + ********************************************************************** 1.12 + */ 1.13 + 1.14 +#include "utypeinfo.h" // for 'typeid' to work 1.15 + 1.16 +#include "unicode/utypes.h" 1.17 + 1.18 +#if !UCONFIG_NO_TRANSLITERATION 1.19 + 1.20 +#include "unicode/putil.h" 1.21 +#include "unicode/translit.h" 1.22 +#include "unicode/locid.h" 1.23 +#include "unicode/msgfmt.h" 1.24 +#include "unicode/rep.h" 1.25 +#include "unicode/resbund.h" 1.26 +#include "unicode/unifilt.h" 1.27 +#include "unicode/uniset.h" 1.28 +#include "unicode/uscript.h" 1.29 +#include "unicode/strenum.h" 1.30 +#include "unicode/utf16.h" 1.31 +#include "cpdtrans.h" 1.32 +#include "nultrans.h" 1.33 +#include "rbt_data.h" 1.34 +#include "rbt_pars.h" 1.35 +#include "rbt.h" 1.36 +#include "transreg.h" 1.37 +#include "name2uni.h" 1.38 +#include "nortrans.h" 1.39 +#include "remtrans.h" 1.40 +#include "titletrn.h" 1.41 +#include "tolowtrn.h" 1.42 +#include "toupptrn.h" 1.43 +#include "uni2name.h" 1.44 +#include "brktrans.h" 1.45 +#include "esctrn.h" 1.46 +#include "unesctrn.h" 1.47 +#include "tridpars.h" 1.48 +#include "anytrans.h" 1.49 +#include "util.h" 1.50 +#include "hash.h" 1.51 +#include "mutex.h" 1.52 +#include "ucln_in.h" 1.53 +#include "uassert.h" 1.54 +#include "cmemory.h" 1.55 +#include "cstring.h" 1.56 +#include "uinvchar.h" 1.57 + 1.58 +static const UChar TARGET_SEP = 0x002D; /*-*/ 1.59 +static const UChar ID_DELIM = 0x003B; /*;*/ 1.60 +static const UChar VARIANT_SEP = 0x002F; // '/' 1.61 + 1.62 +/** 1.63 + * Prefix for resource bundle key for the display name for a 1.64 + * transliterator. The ID is appended to this to form the key. 1.65 + * The resource bundle value should be a String. 1.66 + */ 1.67 +static const char RB_DISPLAY_NAME_PREFIX[] = "%Translit%%"; 1.68 + 1.69 +/** 1.70 + * Prefix for resource bundle key for the display name for a 1.71 + * transliterator SCRIPT. The ID is appended to this to form the key. 1.72 + * The resource bundle value should be a String. 1.73 + */ 1.74 +static const char RB_SCRIPT_DISPLAY_NAME_PREFIX[] = "%Translit%"; 1.75 + 1.76 +/** 1.77 + * Resource bundle key for display name pattern. 1.78 + * The resource bundle value should be a String forming a 1.79 + * MessageFormat pattern, e.g.: 1.80 + * "{0,choice,0#|1#{1} Transliterator|2#{1} to {2} Transliterator}". 1.81 + */ 1.82 +static const char RB_DISPLAY_NAME_PATTERN[] = "TransliteratorNamePattern"; 1.83 + 1.84 +/** 1.85 + * Resource bundle key for the list of RuleBasedTransliterator IDs. 1.86 + * The resource bundle value should be a String[] with each element 1.87 + * being a valid ID. The ID will be appended to RB_RULE_BASED_PREFIX 1.88 + * to obtain the class name in which the RB_RULE key will be sought. 1.89 + */ 1.90 +static const char RB_RULE_BASED_IDS[] = "RuleBasedTransliteratorIDs"; 1.91 + 1.92 +/** 1.93 + * The mutex controlling access to registry object. 1.94 + */ 1.95 +static UMutex registryMutex = U_MUTEX_INITIALIZER; 1.96 + 1.97 +/** 1.98 + * System transliterator registry; non-null when initialized. 1.99 + */ 1.100 +static icu::TransliteratorRegistry* registry = 0; 1.101 + 1.102 +// Macro to check/initialize the registry. ONLY USE WITHIN 1.103 +// MUTEX. Avoids function call when registry is initialized. 1.104 +#define HAVE_REGISTRY(status) (registry!=0 || initializeRegistry(status)) 1.105 + 1.106 +U_NAMESPACE_BEGIN 1.107 + 1.108 +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(Transliterator) 1.109 + 1.110 +/** 1.111 + * Return TRUE if the given UTransPosition is valid for text of 1.112 + * the given length. 1.113 + */ 1.114 +static inline UBool positionIsValid(UTransPosition& index, int32_t len) { 1.115 + return !(index.contextStart < 0 || 1.116 + index.start < index.contextStart || 1.117 + index.limit < index.start || 1.118 + index.contextLimit < index.limit || 1.119 + len < index.contextLimit); 1.120 +} 1.121 + 1.122 +/** 1.123 + * Default constructor. 1.124 + * @param theID the string identifier for this transliterator 1.125 + * @param theFilter the filter. Any character for which 1.126 + * <tt>filter.contains()</tt> returns <tt>FALSE</tt> will not be 1.127 + * altered by this transliterator. If <tt>filter</tt> is 1.128 + * <tt>null</tt> then no filtering is applied. 1.129 + */ 1.130 +Transliterator::Transliterator(const UnicodeString& theID, 1.131 + UnicodeFilter* adoptedFilter) : 1.132 + UObject(), ID(theID), filter(adoptedFilter), 1.133 + maximumContextLength(0) 1.134 +{ 1.135 + // NUL-terminate the ID string, which is a non-aliased copy. 1.136 + ID.append((UChar)0); 1.137 + ID.truncate(ID.length()-1); 1.138 +} 1.139 + 1.140 +/** 1.141 + * Destructor. 1.142 + */ 1.143 +Transliterator::~Transliterator() { 1.144 + if (filter) { 1.145 + delete filter; 1.146 + } 1.147 +} 1.148 + 1.149 +/** 1.150 + * Copy constructor. 1.151 + */ 1.152 +Transliterator::Transliterator(const Transliterator& other) : 1.153 + UObject(other), ID(other.ID), filter(0), 1.154 + maximumContextLength(other.maximumContextLength) 1.155 +{ 1.156 + // NUL-terminate the ID string, which is a non-aliased copy. 1.157 + ID.append((UChar)0); 1.158 + ID.truncate(ID.length()-1); 1.159 + 1.160 + if (other.filter != 0) { 1.161 + // We own the filter, so we must have our own copy 1.162 + filter = (UnicodeFilter*) other.filter->clone(); 1.163 + } 1.164 +} 1.165 + 1.166 +Transliterator* Transliterator::clone() const { 1.167 + return NULL; 1.168 +} 1.169 + 1.170 +/** 1.171 + * Assignment operator. 1.172 + */ 1.173 +Transliterator& Transliterator::operator=(const Transliterator& other) { 1.174 + ID = other.ID; 1.175 + // NUL-terminate the ID string 1.176 + ID.getTerminatedBuffer(); 1.177 + 1.178 + maximumContextLength = other.maximumContextLength; 1.179 + adoptFilter((other.filter == 0) ? 0 : (UnicodeFilter*) other.filter->clone()); 1.180 + return *this; 1.181 +} 1.182 + 1.183 +/** 1.184 + * Transliterates a segment of a string. <code>Transliterator</code> API. 1.185 + * @param text the string to be transliterated 1.186 + * @param start the beginning index, inclusive; <code>0 <= start 1.187 + * <= limit</code>. 1.188 + * @param limit the ending index, exclusive; <code>start <= limit 1.189 + * <= text.length()</code>. 1.190 + * @return the new limit index, or -1 1.191 + */ 1.192 +int32_t Transliterator::transliterate(Replaceable& text, 1.193 + int32_t start, int32_t limit) const { 1.194 + if (start < 0 || 1.195 + limit < start || 1.196 + text.length() < limit) { 1.197 + return -1; 1.198 + } 1.199 + 1.200 + UTransPosition offsets; 1.201 + offsets.contextStart= start; 1.202 + offsets.contextLimit = limit; 1.203 + offsets.start = start; 1.204 + offsets.limit = limit; 1.205 + filteredTransliterate(text, offsets, FALSE, TRUE); 1.206 + return offsets.limit; 1.207 +} 1.208 + 1.209 +/** 1.210 + * Transliterates an entire string in place. Convenience method. 1.211 + * @param text the string to be transliterated 1.212 + */ 1.213 +void Transliterator::transliterate(Replaceable& text) const { 1.214 + transliterate(text, 0, text.length()); 1.215 +} 1.216 + 1.217 +/** 1.218 + * Transliterates the portion of the text buffer that can be 1.219 + * transliterated unambiguosly after new text has been inserted, 1.220 + * typically as a result of a keyboard event. The new text in 1.221 + * <code>insertion</code> will be inserted into <code>text</code> 1.222 + * at <code>index.contextLimit</code>, advancing 1.223 + * <code>index.contextLimit</code> by <code>insertion.length()</code>. 1.224 + * Then the transliterator will try to transliterate characters of 1.225 + * <code>text</code> between <code>index.start</code> and 1.226 + * <code>index.contextLimit</code>. Characters before 1.227 + * <code>index.start</code> will not be changed. 1.228 + * 1.229 + * <p>Upon return, values in <code>index</code> will be updated. 1.230 + * <code>index.contextStart</code> will be advanced to the first 1.231 + * character that future calls to this method will read. 1.232 + * <code>index.start</code> and <code>index.contextLimit</code> will 1.233 + * be adjusted to delimit the range of text that future calls to 1.234 + * this method may change. 1.235 + * 1.236 + * <p>Typical usage of this method begins with an initial call 1.237 + * with <code>index.contextStart</code> and <code>index.contextLimit</code> 1.238 + * set to indicate the portion of <code>text</code> to be 1.239 + * transliterated, and <code>index.start == index.contextStart</code>. 1.240 + * Thereafter, <code>index</code> can be used without 1.241 + * modification in future calls, provided that all changes to 1.242 + * <code>text</code> are made via this method. 1.243 + * 1.244 + * <p>This method assumes that future calls may be made that will 1.245 + * insert new text into the buffer. As a result, it only performs 1.246 + * unambiguous transliterations. After the last call to this 1.247 + * method, there may be untransliterated text that is waiting for 1.248 + * more input to resolve an ambiguity. In order to perform these 1.249 + * pending transliterations, clients should call {@link 1.250 + * #finishKeyboardTransliteration} after the last call to this 1.251 + * method has been made. 1.252 + * 1.253 + * @param text the buffer holding transliterated and untransliterated text 1.254 + * @param index an array of three integers. 1.255 + * 1.256 + * <ul><li><code>index.contextStart</code>: the beginning index, 1.257 + * inclusive; <code>0 <= index.contextStart <= index.contextLimit</code>. 1.258 + * 1.259 + * <li><code>index.contextLimit</code>: the ending index, exclusive; 1.260 + * <code>index.contextStart <= index.contextLimit <= text.length()</code>. 1.261 + * <code>insertion</code> is inserted at 1.262 + * <code>index.contextLimit</code>. 1.263 + * 1.264 + * <li><code>index.start</code>: the next character to be 1.265 + * considered for transliteration; <code>index.contextStart <= 1.266 + * index.start <= index.contextLimit</code>. Characters before 1.267 + * <code>index.start</code> will not be changed by future calls 1.268 + * to this method.</ul> 1.269 + * 1.270 + * @param insertion text to be inserted and possibly 1.271 + * transliterated into the translation buffer at 1.272 + * <code>index.contextLimit</code>. If <code>null</code> then no text 1.273 + * is inserted. 1.274 + * @see #START 1.275 + * @see #LIMIT 1.276 + * @see #CURSOR 1.277 + * @see #handleTransliterate 1.278 + * @exception IllegalArgumentException if <code>index</code> 1.279 + * is invalid 1.280 + */ 1.281 +void Transliterator::transliterate(Replaceable& text, 1.282 + UTransPosition& index, 1.283 + const UnicodeString& insertion, 1.284 + UErrorCode &status) const { 1.285 + _transliterate(text, index, &insertion, status); 1.286 +} 1.287 + 1.288 +/** 1.289 + * Transliterates the portion of the text buffer that can be 1.290 + * transliterated unambiguosly after a new character has been 1.291 + * inserted, typically as a result of a keyboard event. This is a 1.292 + * convenience method; see {@link 1.293 + * #transliterate(Replaceable, int[], String)} for details. 1.294 + * @param text the buffer holding transliterated and 1.295 + * untransliterated text 1.296 + * @param index an array of three integers. See {@link 1.297 + * #transliterate(Replaceable, int[], String)}. 1.298 + * @param insertion text to be inserted and possibly 1.299 + * transliterated into the translation buffer at 1.300 + * <code>index.contextLimit</code>. 1.301 + * @see #transliterate(Replaceable, int[], String) 1.302 + */ 1.303 +void Transliterator::transliterate(Replaceable& text, 1.304 + UTransPosition& index, 1.305 + UChar32 insertion, 1.306 + UErrorCode& status) const { 1.307 + UnicodeString str(insertion); 1.308 + _transliterate(text, index, &str, status); 1.309 +} 1.310 + 1.311 +/** 1.312 + * Transliterates the portion of the text buffer that can be 1.313 + * transliterated unambiguosly. This is a convenience method; see 1.314 + * {@link #transliterate(Replaceable, int[], String)} for 1.315 + * details. 1.316 + * @param text the buffer holding transliterated and 1.317 + * untransliterated text 1.318 + * @param index an array of three integers. See {@link 1.319 + * #transliterate(Replaceable, int[], String)}. 1.320 + * @see #transliterate(Replaceable, int[], String) 1.321 + */ 1.322 +void Transliterator::transliterate(Replaceable& text, 1.323 + UTransPosition& index, 1.324 + UErrorCode& status) const { 1.325 + _transliterate(text, index, 0, status); 1.326 +} 1.327 + 1.328 +/** 1.329 + * Finishes any pending transliterations that were waiting for 1.330 + * more characters. Clients should call this method as the last 1.331 + * call after a sequence of one or more calls to 1.332 + * <code>transliterate()</code>. 1.333 + * @param text the buffer holding transliterated and 1.334 + * untransliterated text. 1.335 + * @param index the array of indices previously passed to {@link 1.336 + * #transliterate} 1.337 + */ 1.338 +void Transliterator::finishTransliteration(Replaceable& text, 1.339 + UTransPosition& index) const { 1.340 + if (!positionIsValid(index, text.length())) { 1.341 + return; 1.342 + } 1.343 + 1.344 + filteredTransliterate(text, index, FALSE, TRUE); 1.345 +} 1.346 + 1.347 +/** 1.348 + * This internal method does keyboard transliteration. If the 1.349 + * 'insertion' is non-null then we append it to 'text' before 1.350 + * proceeding. This method calls through to the pure virtual 1.351 + * framework method handleTransliterate() to do the actual 1.352 + * work. 1.353 + */ 1.354 +void Transliterator::_transliterate(Replaceable& text, 1.355 + UTransPosition& index, 1.356 + const UnicodeString* insertion, 1.357 + UErrorCode &status) const { 1.358 + if (U_FAILURE(status)) { 1.359 + return; 1.360 + } 1.361 + 1.362 + if (!positionIsValid(index, text.length())) { 1.363 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.364 + return; 1.365 + } 1.366 + 1.367 +// int32_t originalStart = index.contextStart; 1.368 + if (insertion != 0) { 1.369 + text.handleReplaceBetween(index.limit, index.limit, *insertion); 1.370 + index.limit += insertion->length(); 1.371 + index.contextLimit += insertion->length(); 1.372 + } 1.373 + 1.374 + if (index.limit > 0 && 1.375 + U16_IS_LEAD(text.charAt(index.limit - 1))) { 1.376 + // Oops, there is a dangling lead surrogate in the buffer. 1.377 + // This will break most transliterators, since they will 1.378 + // assume it is part of a pair. Don't transliterate until 1.379 + // more text comes in. 1.380 + return; 1.381 + } 1.382 + 1.383 + filteredTransliterate(text, index, TRUE, TRUE); 1.384 + 1.385 +#if 0 1.386 + // TODO 1.387 + // I CAN'T DO what I'm attempting below now that the Kleene star 1.388 + // operator is supported. For example, in the rule 1.389 + 1.390 + // ([:Lu:]+) { x } > $1; 1.391 + 1.392 + // what is the maximum context length? getMaximumContextLength() 1.393 + // will return 1, but this is just the length of the ante context 1.394 + // part of the pattern string -- 1 character, which is a standin 1.395 + // for a Quantifier, which contains a StringMatcher, which 1.396 + // contains a UnicodeSet. 1.397 + 1.398 + // There is a complicated way to make this work again, and that's 1.399 + // to add a "maximum left context" protocol into the 1.400 + // UnicodeMatcher hierarchy. At present I'm not convinced this is 1.401 + // worth it. 1.402 + 1.403 + // --- 1.404 + 1.405 + // The purpose of the code below is to keep the context small 1.406 + // while doing incremental transliteration. When part of the left 1.407 + // context (between contextStart and start) is no longer needed, 1.408 + // we try to advance contextStart past that portion. We use the 1.409 + // maximum context length to do so. 1.410 + int32_t newCS = index.start; 1.411 + int32_t n = getMaximumContextLength(); 1.412 + while (newCS > originalStart && n-- > 0) { 1.413 + --newCS; 1.414 + newCS -= U16_LENGTH(text.char32At(newCS)) - 1; 1.415 + } 1.416 + index.contextStart = uprv_max(newCS, originalStart); 1.417 +#endif 1.418 +} 1.419 + 1.420 +/** 1.421 + * This method breaks up the input text into runs of unfiltered 1.422 + * characters. It passes each such run to 1.423 + * <subclass>.handleTransliterate(). Subclasses that can handle the 1.424 + * filter logic more efficiently themselves may override this method. 1.425 + * 1.426 + * All transliteration calls in this class go through this method. 1.427 + */ 1.428 +void Transliterator::filteredTransliterate(Replaceable& text, 1.429 + UTransPosition& index, 1.430 + UBool incremental, 1.431 + UBool rollback) const { 1.432 + // Short circuit path for transliterators with no filter in 1.433 + // non-incremental mode. 1.434 + if (filter == 0 && !rollback) { 1.435 + handleTransliterate(text, index, incremental); 1.436 + return; 1.437 + } 1.438 + 1.439 + //---------------------------------------------------------------------- 1.440 + // This method processes text in two groupings: 1.441 + // 1.442 + // RUNS -- A run is a contiguous group of characters which are contained 1.443 + // in the filter for this transliterator (filter.contains(ch) == TRUE). 1.444 + // Text outside of runs may appear as context but it is not modified. 1.445 + // The start and limit Position values are narrowed to each run. 1.446 + // 1.447 + // PASSES (incremental only) -- To make incremental mode work correctly, 1.448 + // each run is broken up into n passes, where n is the length (in code 1.449 + // points) of the run. Each pass contains the first n characters. If a 1.450 + // pass is completely transliterated, it is committed, and further passes 1.451 + // include characters after the committed text. If a pass is blocked, 1.452 + // and does not transliterate completely, then this method rolls back 1.453 + // the changes made during the pass, extends the pass by one code point, 1.454 + // and tries again. 1.455 + //---------------------------------------------------------------------- 1.456 + 1.457 + // globalLimit is the limit value for the entire operation. We 1.458 + // set index.limit to the end of each unfiltered run before 1.459 + // calling handleTransliterate(), so we need to maintain the real 1.460 + // value of index.limit here. After each transliteration, we 1.461 + // update globalLimit for insertions or deletions that have 1.462 + // happened. 1.463 + int32_t globalLimit = index.limit; 1.464 + 1.465 + // If there is a non-null filter, then break the input text up. Say the 1.466 + // input text has the form: 1.467 + // xxxabcxxdefxx 1.468 + // where 'x' represents a filtered character (filter.contains('x') == 1.469 + // false). Then we break this up into: 1.470 + // xxxabc xxdef xx 1.471 + // Each pass through the loop consumes a run of filtered 1.472 + // characters (which are ignored) and a subsequent run of 1.473 + // unfiltered characters (which are transliterated). 1.474 + 1.475 + for (;;) { 1.476 + 1.477 + if (filter != NULL) { 1.478 + // Narrow the range to be transliterated to the first segment 1.479 + // of unfiltered characters at or after index.start. 1.480 + 1.481 + // Advance past filtered chars 1.482 + UChar32 c; 1.483 + while (index.start < globalLimit && 1.484 + !filter->contains(c=text.char32At(index.start))) { 1.485 + index.start += U16_LENGTH(c); 1.486 + } 1.487 + 1.488 + // Find the end of this run of unfiltered chars 1.489 + index.limit = index.start; 1.490 + while (index.limit < globalLimit && 1.491 + filter->contains(c=text.char32At(index.limit))) { 1.492 + index.limit += U16_LENGTH(c); 1.493 + } 1.494 + } 1.495 + 1.496 + // Check to see if the unfiltered run is empty. This only 1.497 + // happens at the end of the string when all the remaining 1.498 + // characters are filtered. 1.499 + if (index.limit == index.start) { 1.500 + // assert(index.start == globalLimit); 1.501 + break; 1.502 + } 1.503 + 1.504 + // Is this run incremental? If there is additional 1.505 + // filtered text (if limit < globalLimit) then we pass in 1.506 + // an incremental value of FALSE to force the subclass to 1.507 + // complete the transliteration for this run. 1.508 + UBool isIncrementalRun = 1.509 + (index.limit < globalLimit ? FALSE : incremental); 1.510 + 1.511 + int32_t delta; 1.512 + 1.513 + // Implement rollback. To understand the need for rollback, 1.514 + // consider the following transliterator: 1.515 + // 1.516 + // "t" is "a > A;" 1.517 + // "u" is "A > b;" 1.518 + // "v" is a compound of "t; NFD; u" with a filter [:Ll:] 1.519 + // 1.520 + // Now apply "c" to the input text "a". The result is "b". But if 1.521 + // the transliteration is done incrementally, then the NFD holds 1.522 + // things up after "t" has already transformed "a" to "A". When 1.523 + // finishTransliterate() is called, "A" is _not_ processed because 1.524 + // it gets excluded by the [:Ll:] filter, and the end result is "A" 1.525 + // -- incorrect. The problem is that the filter is applied to a 1.526 + // partially-transliterated result, when we only want it to apply to 1.527 + // input text. Although this example hinges on a compound 1.528 + // transliterator containing NFD and a specific filter, it can 1.529 + // actually happen with any transliterator which may do a partial 1.530 + // transformation in incremental mode into characters outside its 1.531 + // filter. 1.532 + // 1.533 + // To handle this, when in incremental mode we supply characters to 1.534 + // handleTransliterate() in several passes. Each pass adds one more 1.535 + // input character to the input text. That is, for input "ABCD", we 1.536 + // first try "A", then "AB", then "ABC", and finally "ABCD". If at 1.537 + // any point we block (upon return, start < limit) then we roll 1.538 + // back. If at any point we complete the run (upon return start == 1.539 + // limit) then we commit that run. 1.540 + 1.541 + if (rollback && isIncrementalRun) { 1.542 + 1.543 + int32_t runStart = index.start; 1.544 + int32_t runLimit = index.limit; 1.545 + int32_t runLength = runLimit - runStart; 1.546 + 1.547 + // Make a rollback copy at the end of the string 1.548 + int32_t rollbackOrigin = text.length(); 1.549 + text.copy(runStart, runLimit, rollbackOrigin); 1.550 + 1.551 + // Variables reflecting the commitment of completely 1.552 + // transliterated text. passStart is the runStart, advanced 1.553 + // past committed text. rollbackStart is the rollbackOrigin, 1.554 + // advanced past rollback text that corresponds to committed 1.555 + // text. 1.556 + int32_t passStart = runStart; 1.557 + int32_t rollbackStart = rollbackOrigin; 1.558 + 1.559 + // The limit for each pass; we advance by one code point with 1.560 + // each iteration. 1.561 + int32_t passLimit = index.start; 1.562 + 1.563 + // Total length, in 16-bit code units, of uncommitted text. 1.564 + // This is the length to be rolled back. 1.565 + int32_t uncommittedLength = 0; 1.566 + 1.567 + // Total delta (change in length) for all passes 1.568 + int32_t totalDelta = 0; 1.569 + 1.570 + // PASS MAIN LOOP -- Start with a single character, and extend 1.571 + // the text by one character at a time. Roll back partial 1.572 + // transliterations and commit complete transliterations. 1.573 + for (;;) { 1.574 + // Length of additional code point, either one or two 1.575 + int32_t charLength = U16_LENGTH(text.char32At(passLimit)); 1.576 + passLimit += charLength; 1.577 + if (passLimit > runLimit) { 1.578 + break; 1.579 + } 1.580 + uncommittedLength += charLength; 1.581 + 1.582 + index.limit = passLimit; 1.583 + 1.584 + // Delegate to subclass for actual transliteration. Upon 1.585 + // return, start will be updated to point after the 1.586 + // transliterated text, and limit and contextLimit will be 1.587 + // adjusted for length changes. 1.588 + handleTransliterate(text, index, TRUE); 1.589 + 1.590 + delta = index.limit - passLimit; // change in length 1.591 + 1.592 + // We failed to completely transliterate this pass. 1.593 + // Roll back the text. Indices remain unchanged; reset 1.594 + // them where necessary. 1.595 + if (index.start != index.limit) { 1.596 + // Find the rollbackStart, adjusted for length changes 1.597 + // and the deletion of partially transliterated text. 1.598 + int32_t rs = rollbackStart + delta - (index.limit - passStart); 1.599 + 1.600 + // Delete the partially transliterated text 1.601 + text.handleReplaceBetween(passStart, index.limit, UnicodeString()); 1.602 + 1.603 + // Copy the rollback text back 1.604 + text.copy(rs, rs + uncommittedLength, passStart); 1.605 + 1.606 + // Restore indices to their original values 1.607 + index.start = passStart; 1.608 + index.limit = passLimit; 1.609 + index.contextLimit -= delta; 1.610 + } 1.611 + 1.612 + // We did completely transliterate this pass. Update the 1.613 + // commit indices to record how far we got. Adjust indices 1.614 + // for length change. 1.615 + else { 1.616 + // Move the pass indices past the committed text. 1.617 + passStart = passLimit = index.start; 1.618 + 1.619 + // Adjust the rollbackStart for length changes and move 1.620 + // it past the committed text. All characters we've 1.621 + // processed to this point are committed now, so zero 1.622 + // out the uncommittedLength. 1.623 + rollbackStart += delta + uncommittedLength; 1.624 + uncommittedLength = 0; 1.625 + 1.626 + // Adjust indices for length changes. 1.627 + runLimit += delta; 1.628 + totalDelta += delta; 1.629 + } 1.630 + } 1.631 + 1.632 + // Adjust overall limit and rollbackOrigin for insertions and 1.633 + // deletions. Don't need to worry about contextLimit because 1.634 + // handleTransliterate() maintains that. 1.635 + rollbackOrigin += totalDelta; 1.636 + globalLimit += totalDelta; 1.637 + 1.638 + // Delete the rollback copy 1.639 + text.handleReplaceBetween(rollbackOrigin, rollbackOrigin + runLength, UnicodeString()); 1.640 + 1.641 + // Move start past committed text 1.642 + index.start = passStart; 1.643 + } 1.644 + 1.645 + else { 1.646 + // Delegate to subclass for actual transliteration. 1.647 + int32_t limit = index.limit; 1.648 + handleTransliterate(text, index, isIncrementalRun); 1.649 + delta = index.limit - limit; // change in length 1.650 + 1.651 + // In a properly written transliterator, start == limit after 1.652 + // handleTransliterate() returns when incremental is false. 1.653 + // Catch cases where the subclass doesn't do this, and throw 1.654 + // an exception. (Just pinning start to limit is a bad idea, 1.655 + // because what's probably happening is that the subclass 1.656 + // isn't transliterating all the way to the end, and it should 1.657 + // in non-incremental mode.) 1.658 + if (!incremental && index.start != index.limit) { 1.659 + // We can't throw an exception, so just fudge things 1.660 + index.start = index.limit; 1.661 + } 1.662 + 1.663 + // Adjust overall limit for insertions/deletions. Don't need 1.664 + // to worry about contextLimit because handleTransliterate() 1.665 + // maintains that. 1.666 + globalLimit += delta; 1.667 + } 1.668 + 1.669 + if (filter == NULL || isIncrementalRun) { 1.670 + break; 1.671 + } 1.672 + 1.673 + // If we did completely transliterate this 1.674 + // run, then repeat with the next unfiltered run. 1.675 + } 1.676 + 1.677 + // Start is valid where it is. Limit needs to be put back where 1.678 + // it was, modulo adjustments for deletions/insertions. 1.679 + index.limit = globalLimit; 1.680 +} 1.681 + 1.682 +void Transliterator::filteredTransliterate(Replaceable& text, 1.683 + UTransPosition& index, 1.684 + UBool incremental) const { 1.685 + filteredTransliterate(text, index, incremental, FALSE); 1.686 +} 1.687 + 1.688 +/** 1.689 + * Method for subclasses to use to set the maximum context length. 1.690 + * @see #getMaximumContextLength 1.691 + */ 1.692 +void Transliterator::setMaximumContextLength(int32_t maxContextLength) { 1.693 + maximumContextLength = maxContextLength; 1.694 +} 1.695 + 1.696 +/** 1.697 + * Returns a programmatic identifier for this transliterator. 1.698 + * If this identifier is passed to <code>getInstance()</code>, it 1.699 + * will return this object, if it has been registered. 1.700 + * @see #registerInstance 1.701 + * @see #getAvailableIDs 1.702 + */ 1.703 +const UnicodeString& Transliterator::getID(void) const { 1.704 + return ID; 1.705 +} 1.706 + 1.707 +/** 1.708 + * Returns a name for this transliterator that is appropriate for 1.709 + * display to the user in the default locale. See {@link 1.710 + * #getDisplayName(Locale)} for details. 1.711 + */ 1.712 +UnicodeString& U_EXPORT2 Transliterator::getDisplayName(const UnicodeString& ID, 1.713 + UnicodeString& result) { 1.714 + return getDisplayName(ID, Locale::getDefault(), result); 1.715 +} 1.716 + 1.717 +/** 1.718 + * Returns a name for this transliterator that is appropriate for 1.719 + * display to the user in the given locale. This name is taken 1.720 + * from the locale resource data in the standard manner of the 1.721 + * <code>java.text</code> package. 1.722 + * 1.723 + * <p>If no localized names exist in the system resource bundles, 1.724 + * a name is synthesized using a localized 1.725 + * <code>MessageFormat</code> pattern from the resource data. The 1.726 + * arguments to this pattern are an integer followed by one or two 1.727 + * strings. The integer is the number of strings, either 1 or 2. 1.728 + * The strings are formed by splitting the ID for this 1.729 + * transliterator at the first TARGET_SEP. If there is no TARGET_SEP, then the 1.730 + * entire ID forms the only string. 1.731 + * @param inLocale the Locale in which the display name should be 1.732 + * localized. 1.733 + * @see java.text.MessageFormat 1.734 + */ 1.735 +UnicodeString& U_EXPORT2 Transliterator::getDisplayName(const UnicodeString& id, 1.736 + const Locale& inLocale, 1.737 + UnicodeString& result) { 1.738 + UErrorCode status = U_ZERO_ERROR; 1.739 + 1.740 + ResourceBundle bundle(U_ICUDATA_TRANSLIT, inLocale, status); 1.741 + 1.742 + // Suspend checking status until later... 1.743 + 1.744 + result.truncate(0); 1.745 + 1.746 + // Normalize the ID 1.747 + UnicodeString source, target, variant; 1.748 + UBool sawSource; 1.749 + TransliteratorIDParser::IDtoSTV(id, source, target, variant, sawSource); 1.750 + if (target.length() < 1) { 1.751 + // No target; malformed id 1.752 + return result; 1.753 + } 1.754 + if (variant.length() > 0) { // Change "Foo" to "/Foo" 1.755 + variant.insert(0, VARIANT_SEP); 1.756 + } 1.757 + UnicodeString ID(source); 1.758 + ID.append(TARGET_SEP).append(target).append(variant); 1.759 + 1.760 + // build the char* key 1.761 + if (uprv_isInvariantUString(ID.getBuffer(), ID.length())) { 1.762 + char key[200]; 1.763 + uprv_strcpy(key, RB_DISPLAY_NAME_PREFIX); 1.764 + int32_t length=(int32_t)uprv_strlen(RB_DISPLAY_NAME_PREFIX); 1.765 + ID.extract(0, (int32_t)(sizeof(key)-length), key+length, (int32_t)(sizeof(key)-length), US_INV); 1.766 + 1.767 + // Try to retrieve a UnicodeString from the bundle. 1.768 + UnicodeString resString = bundle.getStringEx(key, status); 1.769 + 1.770 + if (U_SUCCESS(status) && resString.length() != 0) { 1.771 + return result = resString; // [sic] assign & return 1.772 + } 1.773 + 1.774 +#if !UCONFIG_NO_FORMATTING 1.775 + // We have failed to get a name from the locale data. This is 1.776 + // typical, since most transliterators will not have localized 1.777 + // name data. The next step is to retrieve the MessageFormat 1.778 + // pattern from the locale data and to use it to synthesize the 1.779 + // name from the ID. 1.780 + 1.781 + status = U_ZERO_ERROR; 1.782 + resString = bundle.getStringEx(RB_DISPLAY_NAME_PATTERN, status); 1.783 + 1.784 + if (U_SUCCESS(status) && resString.length() != 0) { 1.785 + MessageFormat msg(resString, inLocale, status); 1.786 + // Suspend checking status until later... 1.787 + 1.788 + // We pass either 2 or 3 Formattable objects to msg. 1.789 + Formattable args[3]; 1.790 + int32_t nargs; 1.791 + args[0].setLong(2); // # of args to follow 1.792 + args[1].setString(source); 1.793 + args[2].setString(target); 1.794 + nargs = 3; 1.795 + 1.796 + // Use display names for the scripts, if they exist 1.797 + UnicodeString s; 1.798 + length=(int32_t)uprv_strlen(RB_SCRIPT_DISPLAY_NAME_PREFIX); 1.799 + for (int j=1; j<=2; ++j) { 1.800 + status = U_ZERO_ERROR; 1.801 + uprv_strcpy(key, RB_SCRIPT_DISPLAY_NAME_PREFIX); 1.802 + args[j].getString(s); 1.803 + if (uprv_isInvariantUString(s.getBuffer(), s.length())) { 1.804 + s.extract(0, sizeof(key)-length-1, key+length, (int32_t)sizeof(key)-length-1, US_INV); 1.805 + 1.806 + resString = bundle.getStringEx(key, status); 1.807 + 1.808 + if (U_SUCCESS(status)) { 1.809 + args[j] = resString; 1.810 + } 1.811 + } 1.812 + } 1.813 + 1.814 + status = U_ZERO_ERROR; 1.815 + FieldPosition pos; // ignored by msg 1.816 + msg.format(args, nargs, result, pos, status); 1.817 + if (U_SUCCESS(status)) { 1.818 + result.append(variant); 1.819 + return result; 1.820 + } 1.821 + } 1.822 +#endif 1.823 + } 1.824 + 1.825 + // We should not reach this point unless there is something 1.826 + // wrong with the build or the RB_DISPLAY_NAME_PATTERN has 1.827 + // been deleted from the root RB_LOCALE_ELEMENTS resource. 1.828 + result = ID; 1.829 + return result; 1.830 +} 1.831 + 1.832 +/** 1.833 + * Returns the filter used by this transliterator, or <tt>null</tt> 1.834 + * if this transliterator uses no filter. Caller musn't delete 1.835 + * the result! 1.836 + */ 1.837 +const UnicodeFilter* Transliterator::getFilter(void) const { 1.838 + return filter; 1.839 +} 1.840 + 1.841 +/** 1.842 + * Returns the filter used by this transliterator, or 1.843 + * <tt>NULL</tt> if this transliterator uses no filter. The 1.844 + * caller must eventually delete the result. After this call, 1.845 + * this transliterator's filter is set to <tt>NULL</tt>. 1.846 + */ 1.847 +UnicodeFilter* Transliterator::orphanFilter(void) { 1.848 + UnicodeFilter *result = filter; 1.849 + filter = NULL; 1.850 + return result; 1.851 +} 1.852 + 1.853 +/** 1.854 + * Changes the filter used by this transliterator. If the filter 1.855 + * is set to <tt>null</tt> then no filtering will occur. 1.856 + * 1.857 + * <p>Callers must take care if a transliterator is in use by 1.858 + * multiple threads. The filter should not be changed by one 1.859 + * thread while another thread may be transliterating. 1.860 + */ 1.861 +void Transliterator::adoptFilter(UnicodeFilter* filterToAdopt) { 1.862 + delete filter; 1.863 + filter = filterToAdopt; 1.864 +} 1.865 + 1.866 +/** 1.867 + * Returns this transliterator's inverse. See the class 1.868 + * documentation for details. This implementation simply inverts 1.869 + * the two entities in the ID and attempts to retrieve the 1.870 + * resulting transliterator. That is, if <code>getID()</code> 1.871 + * returns "A-B", then this method will return the result of 1.872 + * <code>getInstance("B-A")</code>, or <code>null</code> if that 1.873 + * call fails. 1.874 + * 1.875 + * <p>This method does not take filtering into account. The 1.876 + * returned transliterator will have no filter. 1.877 + * 1.878 + * <p>Subclasses with knowledge of their inverse may wish to 1.879 + * override this method. 1.880 + * 1.881 + * @return a transliterator that is an inverse, not necessarily 1.882 + * exact, of this transliterator, or <code>null</code> if no such 1.883 + * transliterator is registered. 1.884 + * @see #registerInstance 1.885 + */ 1.886 +Transliterator* Transliterator::createInverse(UErrorCode& status) const { 1.887 + UParseError parseError; 1.888 + return Transliterator::createInstance(ID, UTRANS_REVERSE,parseError,status); 1.889 +} 1.890 + 1.891 +Transliterator* U_EXPORT2 1.892 +Transliterator::createInstance(const UnicodeString& ID, 1.893 + UTransDirection dir, 1.894 + UErrorCode& status) 1.895 +{ 1.896 + UParseError parseError; 1.897 + return createInstance(ID, dir, parseError, status); 1.898 +} 1.899 + 1.900 +/** 1.901 + * Returns a <code>Transliterator</code> object given its ID. 1.902 + * The ID must be either a system transliterator ID or a ID registered 1.903 + * using <code>registerInstance()</code>. 1.904 + * 1.905 + * @param ID a valid ID, as enumerated by <code>getAvailableIDs()</code> 1.906 + * @return A <code>Transliterator</code> object with the given ID 1.907 + * @see #registerInstance 1.908 + * @see #getAvailableIDs 1.909 + * @see #getID 1.910 + */ 1.911 +Transliterator* U_EXPORT2 1.912 +Transliterator::createInstance(const UnicodeString& ID, 1.913 + UTransDirection dir, 1.914 + UParseError& parseError, 1.915 + UErrorCode& status) 1.916 +{ 1.917 + if (U_FAILURE(status)) { 1.918 + return 0; 1.919 + } 1.920 + 1.921 + UnicodeString canonID; 1.922 + UVector list(status); 1.923 + if (U_FAILURE(status)) { 1.924 + return NULL; 1.925 + } 1.926 + 1.927 + UnicodeSet* globalFilter; 1.928 + // TODO add code for parseError...currently unused, but 1.929 + // later may be used by parsing code... 1.930 + if (!TransliteratorIDParser::parseCompoundID(ID, dir, canonID, list, globalFilter)) { 1.931 + status = U_INVALID_ID; 1.932 + return NULL; 1.933 + } 1.934 + 1.935 + TransliteratorIDParser::instantiateList(list, status); 1.936 + if (U_FAILURE(status)) { 1.937 + return NULL; 1.938 + } 1.939 + 1.940 + U_ASSERT(list.size() > 0); 1.941 + Transliterator* t = NULL; 1.942 + 1.943 + if (list.size() > 1 || canonID.indexOf(ID_DELIM) >= 0) { 1.944 + // [NOTE: If it's a compoundID, we instantiate a CompoundTransliterator even if it only 1.945 + // has one child transliterator. This is so that toRules() will return the right thing 1.946 + // (without any inactive ID), but our main ID still comes out correct. That is, if we 1.947 + // instantiate "(Lower);Latin-Greek;", we want the rules to come out as "::Latin-Greek;" 1.948 + // even though the ID is "(Lower);Latin-Greek;". 1.949 + t = new CompoundTransliterator(list, parseError, status); 1.950 + } 1.951 + else { 1.952 + t = (Transliterator*)list.elementAt(0); 1.953 + } 1.954 + // Check null pointer 1.955 + if (t != NULL) { 1.956 + t->setID(canonID); 1.957 + if (globalFilter != NULL) { 1.958 + t->adoptFilter(globalFilter); 1.959 + } 1.960 + } 1.961 + else if (U_SUCCESS(status)) { 1.962 + status = U_MEMORY_ALLOCATION_ERROR; 1.963 + } 1.964 + return t; 1.965 +} 1.966 + 1.967 +/** 1.968 + * Create a transliterator from a basic ID. This is an ID 1.969 + * containing only the forward direction source, target, and 1.970 + * variant. 1.971 + * @param id a basic ID of the form S-T or S-T/V. 1.972 + * @return a newly created Transliterator or null if the ID is 1.973 + * invalid. 1.974 + */ 1.975 +Transliterator* Transliterator::createBasicInstance(const UnicodeString& id, 1.976 + const UnicodeString* canon) { 1.977 + UParseError pe; 1.978 + UErrorCode ec = U_ZERO_ERROR; 1.979 + TransliteratorAlias* alias = 0; 1.980 + Transliterator* t = 0; 1.981 + 1.982 + umtx_lock(®istryMutex); 1.983 + if (HAVE_REGISTRY(ec)) { 1.984 + t = registry->get(id, alias, ec); 1.985 + } 1.986 + umtx_unlock(®istryMutex); 1.987 + 1.988 + if (U_FAILURE(ec)) { 1.989 + delete t; 1.990 + delete alias; 1.991 + return 0; 1.992 + } 1.993 + 1.994 + // We may have not gotten a transliterator: Because we can't 1.995 + // instantiate a transliterator from inside TransliteratorRegistry:: 1.996 + // get() (that would deadlock), we sometimes pass back an alias. This 1.997 + // contains the data we need to finish the instantiation outside the 1.998 + // registry mutex. The alias may, in turn, generate another alias, so 1.999 + // we handle aliases in a loop. The max times through the loop is two. 1.1000 + // [alan] 1.1001 + while (alias != 0) { 1.1002 + U_ASSERT(t==0); 1.1003 + // Rule-based aliases are handled with TransliteratorAlias:: 1.1004 + // parse(), followed by TransliteratorRegistry::reget(). 1.1005 + // Other aliases are handled with TransliteratorAlias::create(). 1.1006 + if (alias->isRuleBased()) { 1.1007 + // Step 1. parse 1.1008 + TransliteratorParser parser(ec); 1.1009 + alias->parse(parser, pe, ec); 1.1010 + delete alias; 1.1011 + alias = 0; 1.1012 + 1.1013 + // Step 2. reget 1.1014 + umtx_lock(®istryMutex); 1.1015 + if (HAVE_REGISTRY(ec)) { 1.1016 + t = registry->reget(id, parser, alias, ec); 1.1017 + } 1.1018 + umtx_unlock(®istryMutex); 1.1019 + 1.1020 + // Step 3. Loop back around! 1.1021 + } else { 1.1022 + t = alias->create(pe, ec); 1.1023 + delete alias; 1.1024 + alias = 0; 1.1025 + break; 1.1026 + } 1.1027 + if (U_FAILURE(ec)) { 1.1028 + delete t; 1.1029 + delete alias; 1.1030 + t = NULL; 1.1031 + break; 1.1032 + } 1.1033 + } 1.1034 + 1.1035 + if (t != NULL && canon != NULL) { 1.1036 + t->setID(*canon); 1.1037 + } 1.1038 + 1.1039 + return t; 1.1040 +} 1.1041 + 1.1042 +/** 1.1043 + * Returns a <code>Transliterator</code> object constructed from 1.1044 + * the given rule string. This will be a RuleBasedTransliterator, 1.1045 + * if the rule string contains only rules, or a 1.1046 + * CompoundTransliterator, if it contains ID blocks, or a 1.1047 + * NullTransliterator, if it contains ID blocks which parse as 1.1048 + * empty for the given direction. 1.1049 + */ 1.1050 +Transliterator* U_EXPORT2 1.1051 +Transliterator::createFromRules(const UnicodeString& ID, 1.1052 + const UnicodeString& rules, 1.1053 + UTransDirection dir, 1.1054 + UParseError& parseError, 1.1055 + UErrorCode& status) 1.1056 +{ 1.1057 + Transliterator* t = NULL; 1.1058 + 1.1059 + TransliteratorParser parser(status); 1.1060 + parser.parse(rules, dir, parseError, status); 1.1061 + 1.1062 + if (U_FAILURE(status)) { 1.1063 + return 0; 1.1064 + } 1.1065 + 1.1066 + // NOTE: The logic here matches that in TransliteratorRegistry. 1.1067 + if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) { 1.1068 + t = new NullTransliterator(); 1.1069 + } 1.1070 + else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) { 1.1071 + t = new RuleBasedTransliterator(ID, (TransliterationRuleData*)parser.dataVector.orphanElementAt(0), TRUE); 1.1072 + } 1.1073 + else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) { 1.1074 + // idBlock, no data -- this is an alias. The ID has 1.1075 + // been munged from reverse into forward mode, if 1.1076 + // necessary, so instantiate the ID in the forward 1.1077 + // direction. 1.1078 + if (parser.compoundFilter != NULL) { 1.1079 + UnicodeString filterPattern; 1.1080 + parser.compoundFilter->toPattern(filterPattern, FALSE); 1.1081 + t = createInstance(filterPattern + UnicodeString(ID_DELIM) 1.1082 + + *((UnicodeString*)parser.idBlockVector.elementAt(0)), UTRANS_FORWARD, parseError, status); 1.1083 + } 1.1084 + else 1.1085 + t = createInstance(*((UnicodeString*)parser.idBlockVector.elementAt(0)), UTRANS_FORWARD, parseError, status); 1.1086 + 1.1087 + 1.1088 + if (t != NULL) { 1.1089 + t->setID(ID); 1.1090 + } 1.1091 + } 1.1092 + else { 1.1093 + UVector transliterators(status); 1.1094 + int32_t passNumber = 1; 1.1095 + 1.1096 + int32_t limit = parser.idBlockVector.size(); 1.1097 + if (parser.dataVector.size() > limit) 1.1098 + limit = parser.dataVector.size(); 1.1099 + 1.1100 + for (int32_t i = 0; i < limit; i++) { 1.1101 + if (i < parser.idBlockVector.size()) { 1.1102 + UnicodeString* idBlock = (UnicodeString*)parser.idBlockVector.elementAt(i); 1.1103 + if (!idBlock->isEmpty()) { 1.1104 + Transliterator* temp = createInstance(*idBlock, UTRANS_FORWARD, parseError, status); 1.1105 + if (temp != NULL && typeid(*temp) != typeid(NullTransliterator)) 1.1106 + transliterators.addElement(temp, status); 1.1107 + else 1.1108 + delete temp; 1.1109 + } 1.1110 + } 1.1111 + if (!parser.dataVector.isEmpty()) { 1.1112 + TransliterationRuleData* data = (TransliterationRuleData*)parser.dataVector.orphanElementAt(0); 1.1113 + // TODO: Should passNumber be turned into a decimal-string representation (1 -> "1")? 1.1114 + RuleBasedTransliterator* temprbt = new RuleBasedTransliterator(UnicodeString(CompoundTransliterator::PASS_STRING) + UnicodeString(passNumber++), 1.1115 + data, TRUE); 1.1116 + // Check if NULL before adding it to transliterators to avoid future usage of NULL pointer. 1.1117 + if (temprbt == NULL) { 1.1118 + status = U_MEMORY_ALLOCATION_ERROR; 1.1119 + return t; 1.1120 + } 1.1121 + transliterators.addElement(temprbt, status); 1.1122 + } 1.1123 + } 1.1124 + 1.1125 + t = new CompoundTransliterator(transliterators, passNumber - 1, parseError, status); 1.1126 + // Null pointer check 1.1127 + if (t != NULL) { 1.1128 + t->setID(ID); 1.1129 + t->adoptFilter(parser.orphanCompoundFilter()); 1.1130 + } 1.1131 + } 1.1132 + if (U_SUCCESS(status) && t == NULL) { 1.1133 + status = U_MEMORY_ALLOCATION_ERROR; 1.1134 + } 1.1135 + return t; 1.1136 +} 1.1137 + 1.1138 +UnicodeString& Transliterator::toRules(UnicodeString& rulesSource, 1.1139 + UBool escapeUnprintable) const { 1.1140 + // The base class implementation of toRules munges the ID into 1.1141 + // the correct format. That is: foo => ::foo 1.1142 + if (escapeUnprintable) { 1.1143 + rulesSource.truncate(0); 1.1144 + UnicodeString id = getID(); 1.1145 + for (int32_t i=0; i<id.length();) { 1.1146 + UChar32 c = id.char32At(i); 1.1147 + if (!ICU_Utility::escapeUnprintable(rulesSource, c)) { 1.1148 + rulesSource.append(c); 1.1149 + } 1.1150 + i += U16_LENGTH(c); 1.1151 + } 1.1152 + } else { 1.1153 + rulesSource = getID(); 1.1154 + } 1.1155 + // KEEP in sync with rbt_pars 1.1156 + rulesSource.insert(0, UNICODE_STRING_SIMPLE("::")); 1.1157 + rulesSource.append(ID_DELIM); 1.1158 + return rulesSource; 1.1159 +} 1.1160 + 1.1161 +int32_t Transliterator::countElements() const { 1.1162 + const CompoundTransliterator* ct = dynamic_cast<const CompoundTransliterator*>(this); 1.1163 + return ct != NULL ? ct->getCount() : 0; 1.1164 +} 1.1165 + 1.1166 +const Transliterator& Transliterator::getElement(int32_t index, UErrorCode& ec) const { 1.1167 + if (U_FAILURE(ec)) { 1.1168 + return *this; 1.1169 + } 1.1170 + const CompoundTransliterator* cpd = dynamic_cast<const CompoundTransliterator*>(this); 1.1171 + int32_t n = (cpd == NULL) ? 1 : cpd->getCount(); 1.1172 + if (index < 0 || index >= n) { 1.1173 + ec = U_INDEX_OUTOFBOUNDS_ERROR; 1.1174 + return *this; 1.1175 + } else { 1.1176 + return (n == 1) ? *this : cpd->getTransliterator(index); 1.1177 + } 1.1178 +} 1.1179 + 1.1180 +UnicodeSet& Transliterator::getSourceSet(UnicodeSet& result) const { 1.1181 + handleGetSourceSet(result); 1.1182 + if (filter != NULL) { 1.1183 + UnicodeSet* filterSet = dynamic_cast<UnicodeSet*>(filter); 1.1184 + UBool deleteFilterSet = FALSE; 1.1185 + // Most, but not all filters will be UnicodeSets. Optimize for 1.1186 + // the high-runner case. 1.1187 + if (filterSet == NULL) { 1.1188 + filterSet = new UnicodeSet(); 1.1189 + // Check null pointer 1.1190 + if (filterSet == NULL) { 1.1191 + return result; 1.1192 + } 1.1193 + deleteFilterSet = TRUE; 1.1194 + filter->addMatchSetTo(*filterSet); 1.1195 + } 1.1196 + result.retainAll(*filterSet); 1.1197 + if (deleteFilterSet) { 1.1198 + delete filterSet; 1.1199 + } 1.1200 + } 1.1201 + return result; 1.1202 +} 1.1203 + 1.1204 +void Transliterator::handleGetSourceSet(UnicodeSet& result) const { 1.1205 + result.clear(); 1.1206 +} 1.1207 + 1.1208 +UnicodeSet& Transliterator::getTargetSet(UnicodeSet& result) const { 1.1209 + return result.clear(); 1.1210 +} 1.1211 + 1.1212 +// For public consumption 1.1213 +void U_EXPORT2 Transliterator::registerFactory(const UnicodeString& id, 1.1214 + Transliterator::Factory factory, 1.1215 + Transliterator::Token context) { 1.1216 + Mutex lock(®istryMutex); 1.1217 + UErrorCode ec = U_ZERO_ERROR; 1.1218 + if (HAVE_REGISTRY(ec)) { 1.1219 + _registerFactory(id, factory, context); 1.1220 + } 1.1221 +} 1.1222 + 1.1223 +// To be called only by Transliterator subclasses that are called 1.1224 +// to register themselves by initializeRegistry(). 1.1225 +void Transliterator::_registerFactory(const UnicodeString& id, 1.1226 + Transliterator::Factory factory, 1.1227 + Transliterator::Token context) { 1.1228 + UErrorCode ec = U_ZERO_ERROR; 1.1229 + registry->put(id, factory, context, TRUE, ec); 1.1230 +} 1.1231 + 1.1232 +// To be called only by Transliterator subclasses that are called 1.1233 +// to register themselves by initializeRegistry(). 1.1234 +void Transliterator::_registerSpecialInverse(const UnicodeString& target, 1.1235 + const UnicodeString& inverseTarget, 1.1236 + UBool bidirectional) { 1.1237 + UErrorCode status = U_ZERO_ERROR; 1.1238 + TransliteratorIDParser::registerSpecialInverse(target, inverseTarget, bidirectional, status); 1.1239 +} 1.1240 + 1.1241 +/** 1.1242 + * Registers a instance <tt>obj</tt> of a subclass of 1.1243 + * <code>Transliterator</code> with the system. This object must 1.1244 + * implement the <tt>clone()</tt> method. When 1.1245 + * <tt>getInstance()</tt> is called with an ID string that is 1.1246 + * equal to <tt>obj.getID()</tt>, then <tt>obj.clone()</tt> is 1.1247 + * returned. 1.1248 + * 1.1249 + * @param obj an instance of subclass of 1.1250 + * <code>Transliterator</code> that defines <tt>clone()</tt> 1.1251 + * @see #getInstance 1.1252 + * @see #unregister 1.1253 + */ 1.1254 +void U_EXPORT2 Transliterator::registerInstance(Transliterator* adoptedPrototype) { 1.1255 + Mutex lock(®istryMutex); 1.1256 + UErrorCode ec = U_ZERO_ERROR; 1.1257 + if (HAVE_REGISTRY(ec)) { 1.1258 + _registerInstance(adoptedPrototype); 1.1259 + } 1.1260 +} 1.1261 + 1.1262 +void Transliterator::_registerInstance(Transliterator* adoptedPrototype) { 1.1263 + UErrorCode ec = U_ZERO_ERROR; 1.1264 + registry->put(adoptedPrototype, TRUE, ec); 1.1265 +} 1.1266 + 1.1267 +void U_EXPORT2 Transliterator::registerAlias(const UnicodeString& aliasID, 1.1268 + const UnicodeString& realID) { 1.1269 + Mutex lock(®istryMutex); 1.1270 + UErrorCode ec = U_ZERO_ERROR; 1.1271 + if (HAVE_REGISTRY(ec)) { 1.1272 + _registerAlias(aliasID, realID); 1.1273 + } 1.1274 +} 1.1275 + 1.1276 +void Transliterator::_registerAlias(const UnicodeString& aliasID, 1.1277 + const UnicodeString& realID) { 1.1278 + UErrorCode ec = U_ZERO_ERROR; 1.1279 + registry->put(aliasID, realID, FALSE, TRUE, ec); 1.1280 +} 1.1281 + 1.1282 +/** 1.1283 + * Unregisters a transliterator or class. This may be either 1.1284 + * a system transliterator or a user transliterator or class. 1.1285 + * 1.1286 + * @param ID the ID of the transliterator or class 1.1287 + * @see #registerInstance 1.1288 + 1.1289 + */ 1.1290 +void U_EXPORT2 Transliterator::unregister(const UnicodeString& ID) { 1.1291 + Mutex lock(®istryMutex); 1.1292 + UErrorCode ec = U_ZERO_ERROR; 1.1293 + if (HAVE_REGISTRY(ec)) { 1.1294 + registry->remove(ID); 1.1295 + } 1.1296 +} 1.1297 + 1.1298 +/** 1.1299 + * == OBSOLETE - remove in ICU 3.4 == 1.1300 + * Return the number of IDs currently registered with the system. 1.1301 + * To retrieve the actual IDs, call getAvailableID(i) with 1.1302 + * i from 0 to countAvailableIDs() - 1. 1.1303 + */ 1.1304 +int32_t U_EXPORT2 Transliterator::countAvailableIDs(void) { 1.1305 + int32_t retVal = 0; 1.1306 + Mutex lock(®istryMutex); 1.1307 + UErrorCode ec = U_ZERO_ERROR; 1.1308 + if (HAVE_REGISTRY(ec)) { 1.1309 + retVal = registry->countAvailableIDs(); 1.1310 + } 1.1311 + return retVal; 1.1312 +} 1.1313 + 1.1314 +/** 1.1315 + * == OBSOLETE - remove in ICU 3.4 == 1.1316 + * Return the index-th available ID. index must be between 0 1.1317 + * and countAvailableIDs() - 1, inclusive. If index is out of 1.1318 + * range, the result of getAvailableID(0) is returned. 1.1319 + */ 1.1320 +const UnicodeString& U_EXPORT2 Transliterator::getAvailableID(int32_t index) { 1.1321 + const UnicodeString* result = NULL; 1.1322 + umtx_lock(®istryMutex); 1.1323 + UErrorCode ec = U_ZERO_ERROR; 1.1324 + if (HAVE_REGISTRY(ec)) { 1.1325 + result = ®istry->getAvailableID(index); 1.1326 + } 1.1327 + umtx_unlock(®istryMutex); 1.1328 + U_ASSERT(result != NULL); // fail if no registry 1.1329 + return *result; 1.1330 +} 1.1331 + 1.1332 +StringEnumeration* U_EXPORT2 Transliterator::getAvailableIDs(UErrorCode& ec) { 1.1333 + if (U_FAILURE(ec)) return NULL; 1.1334 + StringEnumeration* result = NULL; 1.1335 + umtx_lock(®istryMutex); 1.1336 + if (HAVE_REGISTRY(ec)) { 1.1337 + result = registry->getAvailableIDs(); 1.1338 + } 1.1339 + umtx_unlock(®istryMutex); 1.1340 + if (result == NULL) { 1.1341 + ec = U_INTERNAL_TRANSLITERATOR_ERROR; 1.1342 + } 1.1343 + return result; 1.1344 +} 1.1345 + 1.1346 +int32_t U_EXPORT2 Transliterator::countAvailableSources(void) { 1.1347 + Mutex lock(®istryMutex); 1.1348 + UErrorCode ec = U_ZERO_ERROR; 1.1349 + return HAVE_REGISTRY(ec) ? _countAvailableSources() : 0; 1.1350 +} 1.1351 + 1.1352 +UnicodeString& U_EXPORT2 Transliterator::getAvailableSource(int32_t index, 1.1353 + UnicodeString& result) { 1.1354 + Mutex lock(®istryMutex); 1.1355 + UErrorCode ec = U_ZERO_ERROR; 1.1356 + if (HAVE_REGISTRY(ec)) { 1.1357 + _getAvailableSource(index, result); 1.1358 + } 1.1359 + return result; 1.1360 +} 1.1361 + 1.1362 +int32_t U_EXPORT2 Transliterator::countAvailableTargets(const UnicodeString& source) { 1.1363 + Mutex lock(®istryMutex); 1.1364 + UErrorCode ec = U_ZERO_ERROR; 1.1365 + return HAVE_REGISTRY(ec) ? _countAvailableTargets(source) : 0; 1.1366 +} 1.1367 + 1.1368 +UnicodeString& U_EXPORT2 Transliterator::getAvailableTarget(int32_t index, 1.1369 + const UnicodeString& source, 1.1370 + UnicodeString& result) { 1.1371 + Mutex lock(®istryMutex); 1.1372 + UErrorCode ec = U_ZERO_ERROR; 1.1373 + if (HAVE_REGISTRY(ec)) { 1.1374 + _getAvailableTarget(index, source, result); 1.1375 + } 1.1376 + return result; 1.1377 +} 1.1378 + 1.1379 +int32_t U_EXPORT2 Transliterator::countAvailableVariants(const UnicodeString& source, 1.1380 + const UnicodeString& target) { 1.1381 + Mutex lock(®istryMutex); 1.1382 + UErrorCode ec = U_ZERO_ERROR; 1.1383 + return HAVE_REGISTRY(ec) ? _countAvailableVariants(source, target) : 0; 1.1384 +} 1.1385 + 1.1386 +UnicodeString& U_EXPORT2 Transliterator::getAvailableVariant(int32_t index, 1.1387 + const UnicodeString& source, 1.1388 + const UnicodeString& target, 1.1389 + UnicodeString& result) { 1.1390 + Mutex lock(®istryMutex); 1.1391 + UErrorCode ec = U_ZERO_ERROR; 1.1392 + if (HAVE_REGISTRY(ec)) { 1.1393 + _getAvailableVariant(index, source, target, result); 1.1394 + } 1.1395 + return result; 1.1396 +} 1.1397 + 1.1398 +int32_t Transliterator::_countAvailableSources(void) { 1.1399 + return registry->countAvailableSources(); 1.1400 +} 1.1401 + 1.1402 +UnicodeString& Transliterator::_getAvailableSource(int32_t index, 1.1403 + UnicodeString& result) { 1.1404 + return registry->getAvailableSource(index, result); 1.1405 +} 1.1406 + 1.1407 +int32_t Transliterator::_countAvailableTargets(const UnicodeString& source) { 1.1408 + return registry->countAvailableTargets(source); 1.1409 +} 1.1410 + 1.1411 +UnicodeString& Transliterator::_getAvailableTarget(int32_t index, 1.1412 + const UnicodeString& source, 1.1413 + UnicodeString& result) { 1.1414 + return registry->getAvailableTarget(index, source, result); 1.1415 +} 1.1416 + 1.1417 +int32_t Transliterator::_countAvailableVariants(const UnicodeString& source, 1.1418 + const UnicodeString& target) { 1.1419 + return registry->countAvailableVariants(source, target); 1.1420 +} 1.1421 + 1.1422 +UnicodeString& Transliterator::_getAvailableVariant(int32_t index, 1.1423 + const UnicodeString& source, 1.1424 + const UnicodeString& target, 1.1425 + UnicodeString& result) { 1.1426 + return registry->getAvailableVariant(index, source, target, result); 1.1427 +} 1.1428 + 1.1429 +#ifdef U_USE_DEPRECATED_TRANSLITERATOR_API 1.1430 + 1.1431 +/** 1.1432 + * Method for subclasses to use to obtain a character in the given 1.1433 + * string, with filtering. 1.1434 + * @deprecated the new architecture provides filtering at the top 1.1435 + * level. This method will be removed Dec 31 2001. 1.1436 + */ 1.1437 +UChar Transliterator::filteredCharAt(const Replaceable& text, int32_t i) const { 1.1438 + UChar c; 1.1439 + const UnicodeFilter* localFilter = getFilter(); 1.1440 + return (localFilter == 0) ? text.charAt(i) : 1.1441 + (localFilter->contains(c = text.charAt(i)) ? c : (UChar)0xFFFE); 1.1442 +} 1.1443 + 1.1444 +#endif 1.1445 + 1.1446 +/** 1.1447 + * If the registry is initialized, return TRUE. If not, initialize it 1.1448 + * and return TRUE. If the registry cannot be initialized, return 1.1449 + * FALSE (rare). 1.1450 + * 1.1451 + * IMPORTANT: Upon entry, registryMutex must be LOCKED. The entire 1.1452 + * initialization is done with the lock held. There is NO REASON to 1.1453 + * unlock, since no other thread that is waiting on the registryMutex 1.1454 + * cannot itself proceed until the registry is initialized. 1.1455 + */ 1.1456 +UBool Transliterator::initializeRegistry(UErrorCode &status) { 1.1457 + if (registry != 0) { 1.1458 + return TRUE; 1.1459 + } 1.1460 + 1.1461 + registry = new TransliteratorRegistry(status); 1.1462 + if (registry == 0 || U_FAILURE(status)) { 1.1463 + delete registry; 1.1464 + registry = 0; 1.1465 + return FALSE; // can't create registry, no recovery 1.1466 + } 1.1467 + 1.1468 + /* The following code parses the index table located in 1.1469 + * icu/data/translit/root.txt. The index is an n x 4 table 1.1470 + * that follows this format: 1.1471 + * <id>{ 1.1472 + * file{ 1.1473 + * resource{"<resource>"} 1.1474 + * direction{"<direction>"} 1.1475 + * } 1.1476 + * } 1.1477 + * <id>{ 1.1478 + * internal{ 1.1479 + * resource{"<resource>"} 1.1480 + * direction{"<direction"} 1.1481 + * } 1.1482 + * } 1.1483 + * <id>{ 1.1484 + * alias{"<getInstanceArg"} 1.1485 + * } 1.1486 + * <id> is the ID of the system transliterator being defined. These 1.1487 + * are public IDs enumerated by Transliterator.getAvailableIDs(), 1.1488 + * unless the second field is "internal". 1.1489 + * 1.1490 + * <resource> is a ResourceReader resource name. Currently these refer 1.1491 + * to file names under com/ibm/text/resources. This string is passed 1.1492 + * directly to ResourceReader, together with <encoding>. 1.1493 + * 1.1494 + * <direction> is either "FORWARD" or "REVERSE". 1.1495 + * 1.1496 + * <getInstanceArg> is a string to be passed directly to 1.1497 + * Transliterator.getInstance(). The returned Transliterator object 1.1498 + * then has its ID changed to <id> and is returned. 1.1499 + * 1.1500 + * The extra blank field on "alias" lines is to make the array square. 1.1501 + */ 1.1502 + //static const char translit_index[] = "translit_index"; 1.1503 + 1.1504 + UResourceBundle *bundle, *transIDs, *colBund; 1.1505 + bundle = ures_open(U_ICUDATA_TRANSLIT, NULL/*open default locale*/, &status); 1.1506 + transIDs = ures_getByKey(bundle, RB_RULE_BASED_IDS, 0, &status); 1.1507 + 1.1508 + int32_t row, maxRows; 1.1509 + if (U_SUCCESS(status)) { 1.1510 + maxRows = ures_getSize(transIDs); 1.1511 + for (row = 0; row < maxRows; row++) { 1.1512 + colBund = ures_getByIndex(transIDs, row, 0, &status); 1.1513 + if (U_SUCCESS(status)) { 1.1514 + UnicodeString id(ures_getKey(colBund), -1, US_INV); 1.1515 + UResourceBundle* res = ures_getNextResource(colBund, NULL, &status); 1.1516 + const char* typeStr = ures_getKey(res); 1.1517 + UChar type; 1.1518 + u_charsToUChars(typeStr, &type, 1); 1.1519 + 1.1520 + if (U_SUCCESS(status)) { 1.1521 + int32_t len = 0; 1.1522 + const UChar *resString; 1.1523 + switch (type) { 1.1524 + case 0x66: // 'f' 1.1525 + case 0x69: // 'i' 1.1526 + // 'file' or 'internal'; 1.1527 + // row[2]=resource, row[3]=direction 1.1528 + { 1.1529 + 1.1530 + resString = ures_getStringByKey(res, "resource", &len, &status); 1.1531 + UBool visible = (type == 0x0066 /*f*/); 1.1532 + UTransDirection dir = 1.1533 + (ures_getUnicodeStringByKey(res, "direction", &status).charAt(0) == 1.1534 + 0x0046 /*F*/) ? 1.1535 + UTRANS_FORWARD : UTRANS_REVERSE; 1.1536 + registry->put(id, UnicodeString(TRUE, resString, len), dir, TRUE, visible, status); 1.1537 + } 1.1538 + break; 1.1539 + case 0x61: // 'a' 1.1540 + // 'alias'; row[2]=createInstance argument 1.1541 + resString = ures_getString(res, &len, &status); 1.1542 + registry->put(id, UnicodeString(TRUE, resString, len), TRUE, TRUE, status); 1.1543 + break; 1.1544 + } 1.1545 + } 1.1546 + ures_close(res); 1.1547 + } 1.1548 + ures_close(colBund); 1.1549 + } 1.1550 + } 1.1551 + 1.1552 + ures_close(transIDs); 1.1553 + ures_close(bundle); 1.1554 + 1.1555 + // Manually add prototypes that the system knows about to the 1.1556 + // cache. This is how new non-rule-based transliterators are 1.1557 + // added to the system. 1.1558 + 1.1559 + // This is to allow for null pointer check 1.1560 + NullTransliterator* tempNullTranslit = new NullTransliterator(); 1.1561 + LowercaseTransliterator* tempLowercaseTranslit = new LowercaseTransliterator(); 1.1562 + UppercaseTransliterator* tempUppercaseTranslit = new UppercaseTransliterator(); 1.1563 + TitlecaseTransliterator* tempTitlecaseTranslit = new TitlecaseTransliterator(); 1.1564 + UnicodeNameTransliterator* tempUnicodeTranslit = new UnicodeNameTransliterator(); 1.1565 + NameUnicodeTransliterator* tempNameUnicodeTranslit = new NameUnicodeTransliterator(); 1.1566 +#if !UCONFIG_NO_BREAK_ITERATION 1.1567 + // TODO: could or should these transliterators be referenced polymorphically once constructed? 1.1568 + BreakTransliterator* tempBreakTranslit = new BreakTransliterator(); 1.1569 +#endif 1.1570 + // Check for null pointers 1.1571 + if (tempNullTranslit == NULL || tempLowercaseTranslit == NULL || tempUppercaseTranslit == NULL || 1.1572 + tempTitlecaseTranslit == NULL || tempUnicodeTranslit == NULL || 1.1573 +#if !UCONFIG_NO_BREAK_ITERATION 1.1574 + tempBreakTranslit == NULL || 1.1575 +#endif 1.1576 + tempNameUnicodeTranslit == NULL ) 1.1577 + { 1.1578 + delete tempNullTranslit; 1.1579 + delete tempLowercaseTranslit; 1.1580 + delete tempUppercaseTranslit; 1.1581 + delete tempTitlecaseTranslit; 1.1582 + delete tempUnicodeTranslit; 1.1583 + delete tempNameUnicodeTranslit; 1.1584 +#if !UCONFIG_NO_BREAK_ITERATION 1.1585 + delete tempBreakTranslit; 1.1586 +#endif 1.1587 + // Since there was an error, remove registry 1.1588 + delete registry; 1.1589 + registry = NULL; 1.1590 + 1.1591 + status = U_MEMORY_ALLOCATION_ERROR; 1.1592 + return 0; 1.1593 + } 1.1594 + 1.1595 + registry->put(tempNullTranslit, TRUE, status); 1.1596 + registry->put(tempLowercaseTranslit, TRUE, status); 1.1597 + registry->put(tempUppercaseTranslit, TRUE, status); 1.1598 + registry->put(tempTitlecaseTranslit, TRUE, status); 1.1599 + registry->put(tempUnicodeTranslit, TRUE, status); 1.1600 + registry->put(tempNameUnicodeTranslit, TRUE, status); 1.1601 +#if !UCONFIG_NO_BREAK_ITERATION 1.1602 + registry->put(tempBreakTranslit, FALSE, status); // FALSE means invisible. 1.1603 +#endif 1.1604 + 1.1605 + RemoveTransliterator::registerIDs(); // Must be within mutex 1.1606 + EscapeTransliterator::registerIDs(); 1.1607 + UnescapeTransliterator::registerIDs(); 1.1608 + NormalizationTransliterator::registerIDs(); 1.1609 + AnyTransliterator::registerIDs(); 1.1610 + 1.1611 + _registerSpecialInverse(UNICODE_STRING_SIMPLE("Null"), 1.1612 + UNICODE_STRING_SIMPLE("Null"), FALSE); 1.1613 + _registerSpecialInverse(UNICODE_STRING_SIMPLE("Upper"), 1.1614 + UNICODE_STRING_SIMPLE("Lower"), TRUE); 1.1615 + _registerSpecialInverse(UNICODE_STRING_SIMPLE("Title"), 1.1616 + UNICODE_STRING_SIMPLE("Lower"), FALSE); 1.1617 + 1.1618 + ucln_i18n_registerCleanup(UCLN_I18N_TRANSLITERATOR, utrans_transliterator_cleanup); 1.1619 + 1.1620 + return TRUE; 1.1621 +} 1.1622 + 1.1623 +U_NAMESPACE_END 1.1624 + 1.1625 +// Defined in ucln_in.h: 1.1626 + 1.1627 +/** 1.1628 + * Release all static memory held by transliterator. This will 1.1629 + * necessarily invalidate any rule-based transliterators held by the 1.1630 + * user, because RBTs hold pointers to common data objects. 1.1631 + */ 1.1632 +U_CFUNC UBool utrans_transliterator_cleanup(void) { 1.1633 + U_NAMESPACE_USE 1.1634 + TransliteratorIDParser::cleanup(); 1.1635 + if (registry) { 1.1636 + delete registry; 1.1637 + registry = NULL; 1.1638 + } 1.1639 + return TRUE; 1.1640 +} 1.1641 + 1.1642 +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ 1.1643 + 1.1644 +//eof