diff -r 000000000000 -r 6474c204b198 intl/icu/source/i18n/translit.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/intl/icu/source/i18n/translit.cpp Wed Dec 31 06:09:35 2014 +0100
@@ -0,0 +1,1641 @@
+/*
+ **********************************************************************
+ * Copyright (C) 1999-2012, International Business Machines
+ * Corporation and others. All Rights Reserved.
+ **********************************************************************
+ * Date Name Description
+ * 11/17/99 aliu Creation.
+ **********************************************************************
+ */
+
+#include "utypeinfo.h" // for 'typeid' to work
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_TRANSLITERATION
+
+#include "unicode/putil.h"
+#include "unicode/translit.h"
+#include "unicode/locid.h"
+#include "unicode/msgfmt.h"
+#include "unicode/rep.h"
+#include "unicode/resbund.h"
+#include "unicode/unifilt.h"
+#include "unicode/uniset.h"
+#include "unicode/uscript.h"
+#include "unicode/strenum.h"
+#include "unicode/utf16.h"
+#include "cpdtrans.h"
+#include "nultrans.h"
+#include "rbt_data.h"
+#include "rbt_pars.h"
+#include "rbt.h"
+#include "transreg.h"
+#include "name2uni.h"
+#include "nortrans.h"
+#include "remtrans.h"
+#include "titletrn.h"
+#include "tolowtrn.h"
+#include "toupptrn.h"
+#include "uni2name.h"
+#include "brktrans.h"
+#include "esctrn.h"
+#include "unesctrn.h"
+#include "tridpars.h"
+#include "anytrans.h"
+#include "util.h"
+#include "hash.h"
+#include "mutex.h"
+#include "ucln_in.h"
+#include "uassert.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "uinvchar.h"
+
+static const UChar TARGET_SEP = 0x002D; /*-*/
+static const UChar ID_DELIM = 0x003B; /*;*/
+static const UChar VARIANT_SEP = 0x002F; // '/'
+
+/**
+ * Prefix for resource bundle key for the display name for a
+ * transliterator. The ID is appended to this to form the key.
+ * The resource bundle value should be a String.
+ */
+static const char RB_DISPLAY_NAME_PREFIX[] = "%Translit%%";
+
+/**
+ * Prefix for resource bundle key for the display name for a
+ * transliterator SCRIPT. The ID is appended to this to form the key.
+ * The resource bundle value should be a String.
+ */
+static const char RB_SCRIPT_DISPLAY_NAME_PREFIX[] = "%Translit%";
+
+/**
+ * Resource bundle key for display name pattern.
+ * The resource bundle value should be a String forming a
+ * MessageFormat pattern, e.g.:
+ * "{0,choice,0#|1#{1} Transliterator|2#{1} to {2} Transliterator}".
+ */
+static const char RB_DISPLAY_NAME_PATTERN[] = "TransliteratorNamePattern";
+
+/**
+ * Resource bundle key for the list of RuleBasedTransliterator IDs.
+ * The resource bundle value should be a String[] with each element
+ * being a valid ID. The ID will be appended to RB_RULE_BASED_PREFIX
+ * to obtain the class name in which the RB_RULE key will be sought.
+ */
+static const char RB_RULE_BASED_IDS[] = "RuleBasedTransliteratorIDs";
+
+/**
+ * The mutex controlling access to registry object.
+ */
+static UMutex registryMutex = U_MUTEX_INITIALIZER;
+
+/**
+ * System transliterator registry; non-null when initialized.
+ */
+static icu::TransliteratorRegistry* registry = 0;
+
+// Macro to check/initialize the registry. ONLY USE WITHIN
+// MUTEX. Avoids function call when registry is initialized.
+#define HAVE_REGISTRY(status) (registry!=0 || initializeRegistry(status))
+
+U_NAMESPACE_BEGIN
+
+UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(Transliterator)
+
+/**
+ * Return TRUE if the given UTransPosition is valid for text of
+ * the given length.
+ */
+static inline UBool positionIsValid(UTransPosition& index, int32_t len) {
+ return !(index.contextStart < 0 ||
+ index.start < index.contextStart ||
+ index.limit < index.start ||
+ index.contextLimit < index.limit ||
+ len < index.contextLimit);
+}
+
+/**
+ * Default constructor.
+ * @param theID the string identifier for this transliterator
+ * @param theFilter the filter. Any character for which
+ * filter.contains() returns FALSE will not be
+ * altered by this transliterator. If filter is
+ * null then no filtering is applied.
+ */
+Transliterator::Transliterator(const UnicodeString& theID,
+ UnicodeFilter* adoptedFilter) :
+ UObject(), ID(theID), filter(adoptedFilter),
+ maximumContextLength(0)
+{
+ // NUL-terminate the ID string, which is a non-aliased copy.
+ ID.append((UChar)0);
+ ID.truncate(ID.length()-1);
+}
+
+/**
+ * Destructor.
+ */
+Transliterator::~Transliterator() {
+ if (filter) {
+ delete filter;
+ }
+}
+
+/**
+ * Copy constructor.
+ */
+Transliterator::Transliterator(const Transliterator& other) :
+ UObject(other), ID(other.ID), filter(0),
+ maximumContextLength(other.maximumContextLength)
+{
+ // NUL-terminate the ID string, which is a non-aliased copy.
+ ID.append((UChar)0);
+ ID.truncate(ID.length()-1);
+
+ if (other.filter != 0) {
+ // We own the filter, so we must have our own copy
+ filter = (UnicodeFilter*) other.filter->clone();
+ }
+}
+
+Transliterator* Transliterator::clone() const {
+ return NULL;
+}
+
+/**
+ * Assignment operator.
+ */
+Transliterator& Transliterator::operator=(const Transliterator& other) {
+ ID = other.ID;
+ // NUL-terminate the ID string
+ ID.getTerminatedBuffer();
+
+ maximumContextLength = other.maximumContextLength;
+ adoptFilter((other.filter == 0) ? 0 : (UnicodeFilter*) other.filter->clone());
+ return *this;
+}
+
+/**
+ * Transliterates a segment of a string. Transliterator
API.
+ * @param text the string to be transliterated
+ * @param start the beginning index, inclusive; 0 <= start
+ * <= limit
.
+ * @param limit the ending index, exclusive; start <= limit
+ * <= text.length()
.
+ * @return the new limit index, or -1
+ */
+int32_t Transliterator::transliterate(Replaceable& text,
+ int32_t start, int32_t limit) const {
+ if (start < 0 ||
+ limit < start ||
+ text.length() < limit) {
+ return -1;
+ }
+
+ UTransPosition offsets;
+ offsets.contextStart= start;
+ offsets.contextLimit = limit;
+ offsets.start = start;
+ offsets.limit = limit;
+ filteredTransliterate(text, offsets, FALSE, TRUE);
+ return offsets.limit;
+}
+
+/**
+ * Transliterates an entire string in place. Convenience method.
+ * @param text the string to be transliterated
+ */
+void Transliterator::transliterate(Replaceable& text) const {
+ transliterate(text, 0, text.length());
+}
+
+/**
+ * Transliterates the portion of the text buffer that can be
+ * transliterated unambiguosly after new text has been inserted,
+ * typically as a result of a keyboard event. The new text in
+ * insertion
will be inserted into text
+ * at index.contextLimit
, advancing
+ * index.contextLimit
by insertion.length()
.
+ * Then the transliterator will try to transliterate characters of
+ * text
between index.start
and
+ * index.contextLimit
. Characters before
+ * index.start
will not be changed.
+ *
+ *
Upon return, values in index
will be updated.
+ * index.contextStart
will be advanced to the first
+ * character that future calls to this method will read.
+ * index.start
and index.contextLimit
will
+ * be adjusted to delimit the range of text that future calls to
+ * this method may change.
+ *
+ *
Typical usage of this method begins with an initial call
+ * with index.contextStart
and index.contextLimit
+ * set to indicate the portion of text
to be
+ * transliterated, and index.start == index.contextStart
.
+ * Thereafter, index
can be used without
+ * modification in future calls, provided that all changes to
+ * text
are made via this method.
+ *
+ *
This method assumes that future calls may be made that will + * insert new text into the buffer. As a result, it only performs + * unambiguous transliterations. After the last call to this + * method, there may be untransliterated text that is waiting for + * more input to resolve an ambiguity. In order to perform these + * pending transliterations, clients should call {@link + * #finishKeyboardTransliteration} after the last call to this + * method has been made. + * + * @param text the buffer holding transliterated and untransliterated text + * @param index an array of three integers. + * + *
index.contextStart
: the beginning index,
+ * inclusive; 0 <= index.contextStart <= index.contextLimit
.
+ *
+ * index.contextLimit
: the ending index, exclusive;
+ * index.contextStart <= index.contextLimit <= text.length()
.
+ * insertion
is inserted at
+ * index.contextLimit
.
+ *
+ * index.start
: the next character to be
+ * considered for transliteration; index.contextStart <=
+ * index.start <= index.contextLimit
. Characters before
+ * index.start
will not be changed by future calls
+ * to this method.index.contextLimit
. If null
then no text
+ * is inserted.
+ * @see #START
+ * @see #LIMIT
+ * @see #CURSOR
+ * @see #handleTransliterate
+ * @exception IllegalArgumentException if index
+ * is invalid
+ */
+void Transliterator::transliterate(Replaceable& text,
+ UTransPosition& index,
+ const UnicodeString& insertion,
+ UErrorCode &status) const {
+ _transliterate(text, index, &insertion, status);
+}
+
+/**
+ * Transliterates the portion of the text buffer that can be
+ * transliterated unambiguosly after a new character has been
+ * inserted, typically as a result of a keyboard event. This is a
+ * convenience method; see {@link
+ * #transliterate(Replaceable, int[], String)} for details.
+ * @param text the buffer holding transliterated and
+ * untransliterated text
+ * @param index an array of three integers. See {@link
+ * #transliterate(Replaceable, int[], String)}.
+ * @param insertion text to be inserted and possibly
+ * transliterated into the translation buffer at
+ * index.contextLimit
.
+ * @see #transliterate(Replaceable, int[], String)
+ */
+void Transliterator::transliterate(Replaceable& text,
+ UTransPosition& index,
+ UChar32 insertion,
+ UErrorCode& status) const {
+ UnicodeString str(insertion);
+ _transliterate(text, index, &str, status);
+}
+
+/**
+ * Transliterates the portion of the text buffer that can be
+ * transliterated unambiguosly. This is a convenience method; see
+ * {@link #transliterate(Replaceable, int[], String)} for
+ * details.
+ * @param text the buffer holding transliterated and
+ * untransliterated text
+ * @param index an array of three integers. See {@link
+ * #transliterate(Replaceable, int[], String)}.
+ * @see #transliterate(Replaceable, int[], String)
+ */
+void Transliterator::transliterate(Replaceable& text,
+ UTransPosition& index,
+ UErrorCode& status) const {
+ _transliterate(text, index, 0, status);
+}
+
+/**
+ * Finishes any pending transliterations that were waiting for
+ * more characters. Clients should call this method as the last
+ * call after a sequence of one or more calls to
+ * transliterate()
.
+ * @param text the buffer holding transliterated and
+ * untransliterated text.
+ * @param index the array of indices previously passed to {@link
+ * #transliterate}
+ */
+void Transliterator::finishTransliteration(Replaceable& text,
+ UTransPosition& index) const {
+ if (!positionIsValid(index, text.length())) {
+ return;
+ }
+
+ filteredTransliterate(text, index, FALSE, TRUE);
+}
+
+/**
+ * This internal method does keyboard transliteration. If the
+ * 'insertion' is non-null then we append it to 'text' before
+ * proceeding. This method calls through to the pure virtual
+ * framework method handleTransliterate() to do the actual
+ * work.
+ */
+void Transliterator::_transliterate(Replaceable& text,
+ UTransPosition& index,
+ const UnicodeString* insertion,
+ UErrorCode &status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ if (!positionIsValid(index, text.length())) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+
+// int32_t originalStart = index.contextStart;
+ if (insertion != 0) {
+ text.handleReplaceBetween(index.limit, index.limit, *insertion);
+ index.limit += insertion->length();
+ index.contextLimit += insertion->length();
+ }
+
+ if (index.limit > 0 &&
+ U16_IS_LEAD(text.charAt(index.limit - 1))) {
+ // Oops, there is a dangling lead surrogate in the buffer.
+ // This will break most transliterators, since they will
+ // assume it is part of a pair. Don't transliterate until
+ // more text comes in.
+ return;
+ }
+
+ filteredTransliterate(text, index, TRUE, TRUE);
+
+#if 0
+ // TODO
+ // I CAN'T DO what I'm attempting below now that the Kleene star
+ // operator is supported. For example, in the rule
+
+ // ([:Lu:]+) { x } > $1;
+
+ // what is the maximum context length? getMaximumContextLength()
+ // will return 1, but this is just the length of the ante context
+ // part of the pattern string -- 1 character, which is a standin
+ // for a Quantifier, which contains a StringMatcher, which
+ // contains a UnicodeSet.
+
+ // There is a complicated way to make this work again, and that's
+ // to add a "maximum left context" protocol into the
+ // UnicodeMatcher hierarchy. At present I'm not convinced this is
+ // worth it.
+
+ // ---
+
+ // The purpose of the code below is to keep the context small
+ // while doing incremental transliteration. When part of the left
+ // context (between contextStart and start) is no longer needed,
+ // we try to advance contextStart past that portion. We use the
+ // maximum context length to do so.
+ int32_t newCS = index.start;
+ int32_t n = getMaximumContextLength();
+ while (newCS > originalStart && n-- > 0) {
+ --newCS;
+ newCS -= U16_LENGTH(text.char32At(newCS)) - 1;
+ }
+ index.contextStart = uprv_max(newCS, originalStart);
+#endif
+}
+
+/**
+ * This method breaks up the input text into runs of unfiltered
+ * characters. It passes each such run to
+ * getInstance()
, it
+ * will return this object, if it has been registered.
+ * @see #registerInstance
+ * @see #getAvailableIDs
+ */
+const UnicodeString& Transliterator::getID(void) const {
+ return ID;
+}
+
+/**
+ * Returns a name for this transliterator that is appropriate for
+ * display to the user in the default locale. See {@link
+ * #getDisplayName(Locale)} for details.
+ */
+UnicodeString& U_EXPORT2 Transliterator::getDisplayName(const UnicodeString& ID,
+ UnicodeString& result) {
+ return getDisplayName(ID, Locale::getDefault(), result);
+}
+
+/**
+ * Returns a name for this transliterator that is appropriate for
+ * display to the user in the given locale. This name is taken
+ * from the locale resource data in the standard manner of the
+ * java.text
package.
+ *
+ * If no localized names exist in the system resource bundles,
+ * a name is synthesized using a localized
+ * MessageFormat
pattern from the resource data. The
+ * arguments to this pattern are an integer followed by one or two
+ * strings. The integer is the number of strings, either 1 or 2.
+ * The strings are formed by splitting the ID for this
+ * transliterator at the first TARGET_SEP. If there is no TARGET_SEP, then the
+ * entire ID forms the only string.
+ * @param inLocale the Locale in which the display name should be
+ * localized.
+ * @see java.text.MessageFormat
+ */
+UnicodeString& U_EXPORT2 Transliterator::getDisplayName(const UnicodeString& id,
+ const Locale& inLocale,
+ UnicodeString& result) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ ResourceBundle bundle(U_ICUDATA_TRANSLIT, inLocale, status);
+
+ // Suspend checking status until later...
+
+ result.truncate(0);
+
+ // Normalize the ID
+ UnicodeString source, target, variant;
+ UBool sawSource;
+ TransliteratorIDParser::IDtoSTV(id, source, target, variant, sawSource);
+ if (target.length() < 1) {
+ // No target; malformed id
+ return result;
+ }
+ if (variant.length() > 0) { // Change "Foo" to "/Foo"
+ variant.insert(0, VARIANT_SEP);
+ }
+ UnicodeString ID(source);
+ ID.append(TARGET_SEP).append(target).append(variant);
+
+ // build the char* key
+ if (uprv_isInvariantUString(ID.getBuffer(), ID.length())) {
+ char key[200];
+ uprv_strcpy(key, RB_DISPLAY_NAME_PREFIX);
+ int32_t length=(int32_t)uprv_strlen(RB_DISPLAY_NAME_PREFIX);
+ ID.extract(0, (int32_t)(sizeof(key)-length), key+length, (int32_t)(sizeof(key)-length), US_INV);
+
+ // Try to retrieve a UnicodeString from the bundle.
+ UnicodeString resString = bundle.getStringEx(key, status);
+
+ if (U_SUCCESS(status) && resString.length() != 0) {
+ return result = resString; // [sic] assign & return
+ }
+
+#if !UCONFIG_NO_FORMATTING
+ // We have failed to get a name from the locale data. This is
+ // typical, since most transliterators will not have localized
+ // name data. The next step is to retrieve the MessageFormat
+ // pattern from the locale data and to use it to synthesize the
+ // name from the ID.
+
+ status = U_ZERO_ERROR;
+ resString = bundle.getStringEx(RB_DISPLAY_NAME_PATTERN, status);
+
+ if (U_SUCCESS(status) && resString.length() != 0) {
+ MessageFormat msg(resString, inLocale, status);
+ // Suspend checking status until later...
+
+ // We pass either 2 or 3 Formattable objects to msg.
+ Formattable args[3];
+ int32_t nargs;
+ args[0].setLong(2); // # of args to follow
+ args[1].setString(source);
+ args[2].setString(target);
+ nargs = 3;
+
+ // Use display names for the scripts, if they exist
+ UnicodeString s;
+ length=(int32_t)uprv_strlen(RB_SCRIPT_DISPLAY_NAME_PREFIX);
+ for (int j=1; j<=2; ++j) {
+ status = U_ZERO_ERROR;
+ uprv_strcpy(key, RB_SCRIPT_DISPLAY_NAME_PREFIX);
+ args[j].getString(s);
+ if (uprv_isInvariantUString(s.getBuffer(), s.length())) {
+ s.extract(0, sizeof(key)-length-1, key+length, (int32_t)sizeof(key)-length-1, US_INV);
+
+ resString = bundle.getStringEx(key, status);
+
+ if (U_SUCCESS(status)) {
+ args[j] = resString;
+ }
+ }
+ }
+
+ status = U_ZERO_ERROR;
+ FieldPosition pos; // ignored by msg
+ msg.format(args, nargs, result, pos, status);
+ if (U_SUCCESS(status)) {
+ result.append(variant);
+ return result;
+ }
+ }
+#endif
+ }
+
+ // We should not reach this point unless there is something
+ // wrong with the build or the RB_DISPLAY_NAME_PATTERN has
+ // been deleted from the root RB_LOCALE_ELEMENTS resource.
+ result = ID;
+ return result;
+}
+
+/**
+ * Returns the filter used by this transliterator, or null
+ * if this transliterator uses no filter. Caller musn't delete
+ * the result!
+ */
+const UnicodeFilter* Transliterator::getFilter(void) const {
+ return filter;
+}
+
+/**
+ * Returns the filter used by this transliterator, or
+ * NULL if this transliterator uses no filter. The
+ * caller must eventually delete the result. After this call,
+ * this transliterator's filter is set to NULL.
+ */
+UnicodeFilter* Transliterator::orphanFilter(void) {
+ UnicodeFilter *result = filter;
+ filter = NULL;
+ return result;
+}
+
+/**
+ * Changes the filter used by this transliterator. If the filter
+ * is set to null then no filtering will occur.
+ *
+ *
Callers must take care if a transliterator is in use by
+ * multiple threads. The filter should not be changed by one
+ * thread while another thread may be transliterating.
+ */
+void Transliterator::adoptFilter(UnicodeFilter* filterToAdopt) {
+ delete filter;
+ filter = filterToAdopt;
+}
+
+/**
+ * Returns this transliterator's inverse. See the class
+ * documentation for details. This implementation simply inverts
+ * the two entities in the ID and attempts to retrieve the
+ * resulting transliterator. That is, if getID()
+ * returns "A-B", then this method will return the result of
+ * getInstance("B-A")
, or null
if that
+ * call fails.
+ *
+ *
This method does not take filtering into account. The + * returned transliterator will have no filter. + * + *
Subclasses with knowledge of their inverse may wish to
+ * override this method.
+ *
+ * @return a transliterator that is an inverse, not necessarily
+ * exact, of this transliterator, or null
if no such
+ * transliterator is registered.
+ * @see #registerInstance
+ */
+Transliterator* Transliterator::createInverse(UErrorCode& status) const {
+ UParseError parseError;
+ return Transliterator::createInstance(ID, UTRANS_REVERSE,parseError,status);
+}
+
+Transliterator* U_EXPORT2
+Transliterator::createInstance(const UnicodeString& ID,
+ UTransDirection dir,
+ UErrorCode& status)
+{
+ UParseError parseError;
+ return createInstance(ID, dir, parseError, status);
+}
+
+/**
+ * Returns a Transliterator
object given its ID.
+ * The ID must be either a system transliterator ID or a ID registered
+ * using registerInstance()
.
+ *
+ * @param ID a valid ID, as enumerated by getAvailableIDs()
+ * @return A Transliterator
object with the given ID
+ * @see #registerInstance
+ * @see #getAvailableIDs
+ * @see #getID
+ */
+Transliterator* U_EXPORT2
+Transliterator::createInstance(const UnicodeString& ID,
+ UTransDirection dir,
+ UParseError& parseError,
+ UErrorCode& status)
+{
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+
+ UnicodeString canonID;
+ UVector list(status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ UnicodeSet* globalFilter;
+ // TODO add code for parseError...currently unused, but
+ // later may be used by parsing code...
+ if (!TransliteratorIDParser::parseCompoundID(ID, dir, canonID, list, globalFilter)) {
+ status = U_INVALID_ID;
+ return NULL;
+ }
+
+ TransliteratorIDParser::instantiateList(list, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ U_ASSERT(list.size() > 0);
+ Transliterator* t = NULL;
+
+ if (list.size() > 1 || canonID.indexOf(ID_DELIM) >= 0) {
+ // [NOTE: If it's a compoundID, we instantiate a CompoundTransliterator even if it only
+ // has one child transliterator. This is so that toRules() will return the right thing
+ // (without any inactive ID), but our main ID still comes out correct. That is, if we
+ // instantiate "(Lower);Latin-Greek;", we want the rules to come out as "::Latin-Greek;"
+ // even though the ID is "(Lower);Latin-Greek;".
+ t = new CompoundTransliterator(list, parseError, status);
+ }
+ else {
+ t = (Transliterator*)list.elementAt(0);
+ }
+ // Check null pointer
+ if (t != NULL) {
+ t->setID(canonID);
+ if (globalFilter != NULL) {
+ t->adoptFilter(globalFilter);
+ }
+ }
+ else if (U_SUCCESS(status)) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ return t;
+}
+
+/**
+ * Create a transliterator from a basic ID. This is an ID
+ * containing only the forward direction source, target, and
+ * variant.
+ * @param id a basic ID of the form S-T or S-T/V.
+ * @return a newly created Transliterator or null if the ID is
+ * invalid.
+ */
+Transliterator* Transliterator::createBasicInstance(const UnicodeString& id,
+ const UnicodeString* canon) {
+ UParseError pe;
+ UErrorCode ec = U_ZERO_ERROR;
+ TransliteratorAlias* alias = 0;
+ Transliterator* t = 0;
+
+ umtx_lock(®istryMutex);
+ if (HAVE_REGISTRY(ec)) {
+ t = registry->get(id, alias, ec);
+ }
+ umtx_unlock(®istryMutex);
+
+ if (U_FAILURE(ec)) {
+ delete t;
+ delete alias;
+ return 0;
+ }
+
+ // We may have not gotten a transliterator: Because we can't
+ // instantiate a transliterator from inside TransliteratorRegistry::
+ // get() (that would deadlock), we sometimes pass back an alias. This
+ // contains the data we need to finish the instantiation outside the
+ // registry mutex. The alias may, in turn, generate another alias, so
+ // we handle aliases in a loop. The max times through the loop is two.
+ // [alan]
+ while (alias != 0) {
+ U_ASSERT(t==0);
+ // Rule-based aliases are handled with TransliteratorAlias::
+ // parse(), followed by TransliteratorRegistry::reget().
+ // Other aliases are handled with TransliteratorAlias::create().
+ if (alias->isRuleBased()) {
+ // Step 1. parse
+ TransliteratorParser parser(ec);
+ alias->parse(parser, pe, ec);
+ delete alias;
+ alias = 0;
+
+ // Step 2. reget
+ umtx_lock(®istryMutex);
+ if (HAVE_REGISTRY(ec)) {
+ t = registry->reget(id, parser, alias, ec);
+ }
+ umtx_unlock(®istryMutex);
+
+ // Step 3. Loop back around!
+ } else {
+ t = alias->create(pe, ec);
+ delete alias;
+ alias = 0;
+ break;
+ }
+ if (U_FAILURE(ec)) {
+ delete t;
+ delete alias;
+ t = NULL;
+ break;
+ }
+ }
+
+ if (t != NULL && canon != NULL) {
+ t->setID(*canon);
+ }
+
+ return t;
+}
+
+/**
+ * Returns a Transliterator
object constructed from
+ * the given rule string. This will be a RuleBasedTransliterator,
+ * if the rule string contains only rules, or a
+ * CompoundTransliterator, if it contains ID blocks, or a
+ * NullTransliterator, if it contains ID blocks which parse as
+ * empty for the given direction.
+ */
+Transliterator* U_EXPORT2
+Transliterator::createFromRules(const UnicodeString& ID,
+ const UnicodeString& rules,
+ UTransDirection dir,
+ UParseError& parseError,
+ UErrorCode& status)
+{
+ Transliterator* t = NULL;
+
+ TransliteratorParser parser(status);
+ parser.parse(rules, dir, parseError, status);
+
+ if (U_FAILURE(status)) {
+ return 0;
+ }
+
+ // NOTE: The logic here matches that in TransliteratorRegistry.
+ if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) {
+ t = new NullTransliterator();
+ }
+ else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) {
+ t = new RuleBasedTransliterator(ID, (TransliterationRuleData*)parser.dataVector.orphanElementAt(0), TRUE);
+ }
+ else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) {
+ // idBlock, no data -- this is an alias. The ID has
+ // been munged from reverse into forward mode, if
+ // necessary, so instantiate the ID in the forward
+ // direction.
+ if (parser.compoundFilter != NULL) {
+ UnicodeString filterPattern;
+ parser.compoundFilter->toPattern(filterPattern, FALSE);
+ t = createInstance(filterPattern + UnicodeString(ID_DELIM)
+ + *((UnicodeString*)parser.idBlockVector.elementAt(0)), UTRANS_FORWARD, parseError, status);
+ }
+ else
+ t = createInstance(*((UnicodeString*)parser.idBlockVector.elementAt(0)), UTRANS_FORWARD, parseError, status);
+
+
+ if (t != NULL) {
+ t->setID(ID);
+ }
+ }
+ else {
+ UVector transliterators(status);
+ int32_t passNumber = 1;
+
+ int32_t limit = parser.idBlockVector.size();
+ if (parser.dataVector.size() > limit)
+ limit = parser.dataVector.size();
+
+ for (int32_t i = 0; i < limit; i++) {
+ if (i < parser.idBlockVector.size()) {
+ UnicodeString* idBlock = (UnicodeString*)parser.idBlockVector.elementAt(i);
+ if (!idBlock->isEmpty()) {
+ Transliterator* temp = createInstance(*idBlock, UTRANS_FORWARD, parseError, status);
+ if (temp != NULL && typeid(*temp) != typeid(NullTransliterator))
+ transliterators.addElement(temp, status);
+ else
+ delete temp;
+ }
+ }
+ if (!parser.dataVector.isEmpty()) {
+ TransliterationRuleData* data = (TransliterationRuleData*)parser.dataVector.orphanElementAt(0);
+ // TODO: Should passNumber be turned into a decimal-string representation (1 -> "1")?
+ RuleBasedTransliterator* temprbt = new RuleBasedTransliterator(UnicodeString(CompoundTransliterator::PASS_STRING) + UnicodeString(passNumber++),
+ data, TRUE);
+ // Check if NULL before adding it to transliterators to avoid future usage of NULL pointer.
+ if (temprbt == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return t;
+ }
+ transliterators.addElement(temprbt, status);
+ }
+ }
+
+ t = new CompoundTransliterator(transliterators, passNumber - 1, parseError, status);
+ // Null pointer check
+ if (t != NULL) {
+ t->setID(ID);
+ t->adoptFilter(parser.orphanCompoundFilter());
+ }
+ }
+ if (U_SUCCESS(status) && t == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ return t;
+}
+
+UnicodeString& Transliterator::toRules(UnicodeString& rulesSource,
+ UBool escapeUnprintable) const {
+ // The base class implementation of toRules munges the ID into
+ // the correct format. That is: foo => ::foo
+ if (escapeUnprintable) {
+ rulesSource.truncate(0);
+ UnicodeString id = getID();
+ for (int32_t i=0; iTransliterator
with the system. This object must
+ * implement the clone() method. When
+ * getInstance() is called with an ID string that is
+ * equal to obj.getID(), then obj.clone() is
+ * returned.
+ *
+ * @param obj an instance of subclass of
+ * Transliterator
that defines clone()
+ * @see #getInstance
+ * @see #unregister
+ */
+void U_EXPORT2 Transliterator::registerInstance(Transliterator* adoptedPrototype) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ _registerInstance(adoptedPrototype);
+ }
+}
+
+void Transliterator::_registerInstance(Transliterator* adoptedPrototype) {
+ UErrorCode ec = U_ZERO_ERROR;
+ registry->put(adoptedPrototype, TRUE, ec);
+}
+
+void U_EXPORT2 Transliterator::registerAlias(const UnicodeString& aliasID,
+ const UnicodeString& realID) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ _registerAlias(aliasID, realID);
+ }
+}
+
+void Transliterator::_registerAlias(const UnicodeString& aliasID,
+ const UnicodeString& realID) {
+ UErrorCode ec = U_ZERO_ERROR;
+ registry->put(aliasID, realID, FALSE, TRUE, ec);
+}
+
+/**
+ * Unregisters a transliterator or class. This may be either
+ * a system transliterator or a user transliterator or class.
+ *
+ * @param ID the ID of the transliterator or class
+ * @see #registerInstance
+
+ */
+void U_EXPORT2 Transliterator::unregister(const UnicodeString& ID) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ registry->remove(ID);
+ }
+}
+
+/**
+ * == OBSOLETE - remove in ICU 3.4 ==
+ * Return the number of IDs currently registered with the system.
+ * To retrieve the actual IDs, call getAvailableID(i) with
+ * i from 0 to countAvailableIDs() - 1.
+ */
+int32_t U_EXPORT2 Transliterator::countAvailableIDs(void) {
+ int32_t retVal = 0;
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ retVal = registry->countAvailableIDs();
+ }
+ return retVal;
+}
+
+/**
+ * == OBSOLETE - remove in ICU 3.4 ==
+ * Return the index-th available ID. index must be between 0
+ * and countAvailableIDs() - 1, inclusive. If index is out of
+ * range, the result of getAvailableID(0) is returned.
+ */
+const UnicodeString& U_EXPORT2 Transliterator::getAvailableID(int32_t index) {
+ const UnicodeString* result = NULL;
+ umtx_lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ result = ®istry->getAvailableID(index);
+ }
+ umtx_unlock(®istryMutex);
+ U_ASSERT(result != NULL); // fail if no registry
+ return *result;
+}
+
+StringEnumeration* U_EXPORT2 Transliterator::getAvailableIDs(UErrorCode& ec) {
+ if (U_FAILURE(ec)) return NULL;
+ StringEnumeration* result = NULL;
+ umtx_lock(®istryMutex);
+ if (HAVE_REGISTRY(ec)) {
+ result = registry->getAvailableIDs();
+ }
+ umtx_unlock(®istryMutex);
+ if (result == NULL) {
+ ec = U_INTERNAL_TRANSLITERATOR_ERROR;
+ }
+ return result;
+}
+
+int32_t U_EXPORT2 Transliterator::countAvailableSources(void) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ return HAVE_REGISTRY(ec) ? _countAvailableSources() : 0;
+}
+
+UnicodeString& U_EXPORT2 Transliterator::getAvailableSource(int32_t index,
+ UnicodeString& result) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ _getAvailableSource(index, result);
+ }
+ return result;
+}
+
+int32_t U_EXPORT2 Transliterator::countAvailableTargets(const UnicodeString& source) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ return HAVE_REGISTRY(ec) ? _countAvailableTargets(source) : 0;
+}
+
+UnicodeString& U_EXPORT2 Transliterator::getAvailableTarget(int32_t index,
+ const UnicodeString& source,
+ UnicodeString& result) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ _getAvailableTarget(index, source, result);
+ }
+ return result;
+}
+
+int32_t U_EXPORT2 Transliterator::countAvailableVariants(const UnicodeString& source,
+ const UnicodeString& target) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ return HAVE_REGISTRY(ec) ? _countAvailableVariants(source, target) : 0;
+}
+
+UnicodeString& U_EXPORT2 Transliterator::getAvailableVariant(int32_t index,
+ const UnicodeString& source,
+ const UnicodeString& target,
+ UnicodeString& result) {
+ Mutex lock(®istryMutex);
+ UErrorCode ec = U_ZERO_ERROR;
+ if (HAVE_REGISTRY(ec)) {
+ _getAvailableVariant(index, source, target, result);
+ }
+ return result;
+}
+
+int32_t Transliterator::_countAvailableSources(void) {
+ return registry->countAvailableSources();
+}
+
+UnicodeString& Transliterator::_getAvailableSource(int32_t index,
+ UnicodeString& result) {
+ return registry->getAvailableSource(index, result);
+}
+
+int32_t Transliterator::_countAvailableTargets(const UnicodeString& source) {
+ return registry->countAvailableTargets(source);
+}
+
+UnicodeString& Transliterator::_getAvailableTarget(int32_t index,
+ const UnicodeString& source,
+ UnicodeString& result) {
+ return registry->getAvailableTarget(index, source, result);
+}
+
+int32_t Transliterator::_countAvailableVariants(const UnicodeString& source,
+ const UnicodeString& target) {
+ return registry->countAvailableVariants(source, target);
+}
+
+UnicodeString& Transliterator::_getAvailableVariant(int32_t index,
+ const UnicodeString& source,
+ const UnicodeString& target,
+ UnicodeString& result) {
+ return registry->getAvailableVariant(index, source, target, result);
+}
+
+#ifdef U_USE_DEPRECATED_TRANSLITERATOR_API
+
+/**
+ * Method for subclasses to use to obtain a character in the given
+ * string, with filtering.
+ * @deprecated the new architecture provides filtering at the top
+ * level. This method will be removed Dec 31 2001.
+ */
+UChar Transliterator::filteredCharAt(const Replaceable& text, int32_t i) const {
+ UChar c;
+ const UnicodeFilter* localFilter = getFilter();
+ return (localFilter == 0) ? text.charAt(i) :
+ (localFilter->contains(c = text.charAt(i)) ? c : (UChar)0xFFFE);
+}
+
+#endif
+
+/**
+ * If the registry is initialized, return TRUE. If not, initialize it
+ * and return TRUE. If the registry cannot be initialized, return
+ * FALSE (rare).
+ *
+ * IMPORTANT: Upon entry, registryMutex must be LOCKED. The entire
+ * initialization is done with the lock held. There is NO REASON to
+ * unlock, since no other thread that is waiting on the registryMutex
+ * cannot itself proceed until the registry is initialized.
+ */
+UBool Transliterator::initializeRegistry(UErrorCode &status) {
+ if (registry != 0) {
+ return TRUE;
+ }
+
+ registry = new TransliteratorRegistry(status);
+ if (registry == 0 || U_FAILURE(status)) {
+ delete registry;
+ registry = 0;
+ return FALSE; // can't create registry, no recovery
+ }
+
+ /* The following code parses the index table located in
+ * icu/data/translit/root.txt. The index is an n x 4 table
+ * that follows this format:
+ *