1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678 |
- // © 2016 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- /*
- **********************************************************************
- * Copyright (C) 1999-2016, 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 char16_t TARGET_SEP = 0x002D; /*-*/
- static const char16_t ID_DELIM = 0x003B; /*;*/
- static const char16_t 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 icu::UMutex registryMutex;
- /**
- * 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
- * <tt>filter.contains()</tt> returns <tt>false</tt> will not be
- * altered by this transliterator. If <tt>filter</tt> is
- * <tt>null</tt> 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((char16_t)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((char16_t)0);
- ID.truncate(ID.length()-1);
- if (other.filter != 0) {
- // We own the filter, so we must have our own copy
- filter = other.filter->clone();
- }
- }
- Transliterator* Transliterator::clone() const {
- return nullptr;
- }
- /**
- * Assignment operator.
- */
- Transliterator& Transliterator::operator=(const Transliterator& other) {
- if (this == &other) { return *this; } // self-assignment: no-op
- ID = other.ID;
- // NUL-terminate the ID string
- ID.getTerminatedBuffer();
- maximumContextLength = other.maximumContextLength;
- adoptFilter((other.filter == 0) ? 0 : other.filter->clone());
- return *this;
- }
- /**
- * Transliterates a segment of a string. <code>Transliterator</code> API.
- * @param text the string to be transliterated
- * @param start the beginning index, inclusive; <code>0 <= start
- * <= limit</code>.
- * @param limit the ending index, exclusive; <code>start <= limit
- * <= text.length()</code>.
- * @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
- * <code>insertion</code> will be inserted into <code>text</code>
- * at <code>index.contextLimit</code>, advancing
- * <code>index.contextLimit</code> by <code>insertion.length()</code>.
- * Then the transliterator will try to transliterate characters of
- * <code>text</code> between <code>index.start</code> and
- * <code>index.contextLimit</code>. Characters before
- * <code>index.start</code> will not be changed.
- *
- * <p>Upon return, values in <code>index</code> will be updated.
- * <code>index.contextStart</code> will be advanced to the first
- * character that future calls to this method will read.
- * <code>index.start</code> and <code>index.contextLimit</code> will
- * be adjusted to delimit the range of text that future calls to
- * this method may change.
- *
- * <p>Typical usage of this method begins with an initial call
- * with <code>index.contextStart</code> and <code>index.contextLimit</code>
- * set to indicate the portion of <code>text</code> to be
- * transliterated, and <code>index.start == index.contextStart</code>.
- * Thereafter, <code>index</code> can be used without
- * modification in future calls, provided that all changes to
- * <code>text</code> are made via this method.
- *
- * <p>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.
- *
- * <ul><li><code>index.contextStart</code>: the beginning index,
- * inclusive; <code>0 <= index.contextStart <= index.contextLimit</code>.
- *
- * <li><code>index.contextLimit</code>: the ending index, exclusive;
- * <code>index.contextStart <= index.contextLimit <= text.length()</code>.
- * <code>insertion</code> is inserted at
- * <code>index.contextLimit</code>.
- *
- * <li><code>index.start</code>: the next character to be
- * considered for transliteration; <code>index.contextStart <=
- * index.start <= index.contextLimit</code>. Characters before
- * <code>index.start</code> will not be changed by future calls
- * to this method.</ul>
- *
- * @param insertion text to be inserted and possibly
- * transliterated into the translation buffer at
- * <code>index.contextLimit</code>. If <code>null</code> then no text
- * is inserted.
- * @see #START
- * @see #LIMIT
- * @see #CURSOR
- * @see #handleTransliterate
- * @exception IllegalArgumentException if <code>index</code>
- * 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
- * <code>index.contextLimit</code>.
- * @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
- * <code>transliterate()</code>.
- * @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
- * <subclass>.handleTransliterate(). Subclasses that can handle the
- * filter logic more efficiently themselves may override this method.
- *
- * All transliteration calls in this class go through this method.
- */
- void Transliterator::filteredTransliterate(Replaceable& text,
- UTransPosition& index,
- UBool incremental,
- UBool rollback) const {
- // Short circuit path for transliterators with no filter in
- // non-incremental mode.
- if (filter == 0 && !rollback) {
- handleTransliterate(text, index, incremental);
- return;
- }
- //----------------------------------------------------------------------
- // This method processes text in two groupings:
- //
- // RUNS -- A run is a contiguous group of characters which are contained
- // in the filter for this transliterator (filter.contains(ch) == true).
- // Text outside of runs may appear as context but it is not modified.
- // The start and limit Position values are narrowed to each run.
- //
- // PASSES (incremental only) -- To make incremental mode work correctly,
- // each run is broken up into n passes, where n is the length (in code
- // points) of the run. Each pass contains the first n characters. If a
- // pass is completely transliterated, it is committed, and further passes
- // include characters after the committed text. If a pass is blocked,
- // and does not transliterate completely, then this method rolls back
- // the changes made during the pass, extends the pass by one code point,
- // and tries again.
- //----------------------------------------------------------------------
-
- // globalLimit is the limit value for the entire operation. We
- // set index.limit to the end of each unfiltered run before
- // calling handleTransliterate(), so we need to maintain the real
- // value of index.limit here. After each transliteration, we
- // update globalLimit for insertions or deletions that have
- // happened.
- int32_t globalLimit = index.limit;
-
- // If there is a non-null filter, then break the input text up. Say the
- // input text has the form:
- // xxxabcxxdefxx
- // where 'x' represents a filtered character (filter.contains('x') ==
- // false). Then we break this up into:
- // xxxabc xxdef xx
- // Each pass through the loop consumes a run of filtered
- // characters (which are ignored) and a subsequent run of
- // unfiltered characters (which are transliterated).
-
- for (;;) {
- if (filter != nullptr) {
- // Narrow the range to be transliterated to the first segment
- // of unfiltered characters at or after index.start.
- // Advance past filtered chars
- UChar32 c;
- while (index.start < globalLimit &&
- !filter->contains(c=text.char32At(index.start))) {
- index.start += U16_LENGTH(c);
- }
- // Find the end of this run of unfiltered chars
- index.limit = index.start;
- while (index.limit < globalLimit &&
- filter->contains(c=text.char32At(index.limit))) {
- index.limit += U16_LENGTH(c);
- }
- }
- // Check to see if the unfiltered run is empty. This only
- // happens at the end of the string when all the remaining
- // characters are filtered.
- if (index.limit == index.start) {
- // assert(index.start == globalLimit);
- break;
- }
- // Is this run incremental? If there is additional
- // filtered text (if limit < globalLimit) then we pass in
- // an incremental value of false to force the subclass to
- // complete the transliteration for this run.
- UBool isIncrementalRun =
- (index.limit < globalLimit ? false : incremental);
-
- int32_t delta;
- // Implement rollback. To understand the need for rollback,
- // consider the following transliterator:
- //
- // "t" is "a > A;"
- // "u" is "A > b;"
- // "v" is a compound of "t; NFD; u" with a filter [:Ll:]
- //
- // Now apply "c" to the input text "a". The result is "b". But if
- // the transliteration is done incrementally, then the NFD holds
- // things up after "t" has already transformed "a" to "A". When
- // finishTransliterate() is called, "A" is _not_ processed because
- // it gets excluded by the [:Ll:] filter, and the end result is "A"
- // -- incorrect. The problem is that the filter is applied to a
- // partially-transliterated result, when we only want it to apply to
- // input text. Although this example hinges on a compound
- // transliterator containing NFD and a specific filter, it can
- // actually happen with any transliterator which may do a partial
- // transformation in incremental mode into characters outside its
- // filter.
- //
- // To handle this, when in incremental mode we supply characters to
- // handleTransliterate() in several passes. Each pass adds one more
- // input character to the input text. That is, for input "ABCD", we
- // first try "A", then "AB", then "ABC", and finally "ABCD". If at
- // any point we block (upon return, start < limit) then we roll
- // back. If at any point we complete the run (upon return start ==
- // limit) then we commit that run.
- if (rollback && isIncrementalRun) {
- int32_t runStart = index.start;
- int32_t runLimit = index.limit;
- int32_t runLength = runLimit - runStart;
- // Make a rollback copy at the end of the string
- int32_t rollbackOrigin = text.length();
- text.copy(runStart, runLimit, rollbackOrigin);
- // Variables reflecting the commitment of completely
- // transliterated text. passStart is the runStart, advanced
- // past committed text. rollbackStart is the rollbackOrigin,
- // advanced past rollback text that corresponds to committed
- // text.
- int32_t passStart = runStart;
- int32_t rollbackStart = rollbackOrigin;
- // The limit for each pass; we advance by one code point with
- // each iteration.
- int32_t passLimit = index.start;
- // Total length, in 16-bit code units, of uncommitted text.
- // This is the length to be rolled back.
- int32_t uncommittedLength = 0;
- // Total delta (change in length) for all passes
- int32_t totalDelta = 0;
- // PASS MAIN LOOP -- Start with a single character, and extend
- // the text by one character at a time. Roll back partial
- // transliterations and commit complete transliterations.
- for (;;) {
- // Length of additional code point, either one or two
- int32_t charLength = U16_LENGTH(text.char32At(passLimit));
- passLimit += charLength;
- if (passLimit > runLimit) {
- break;
- }
- uncommittedLength += charLength;
- index.limit = passLimit;
- // Delegate to subclass for actual transliteration. Upon
- // return, start will be updated to point after the
- // transliterated text, and limit and contextLimit will be
- // adjusted for length changes.
- handleTransliterate(text, index, true);
- delta = index.limit - passLimit; // change in length
- // We failed to completely transliterate this pass.
- // Roll back the text. Indices remain unchanged; reset
- // them where necessary.
- if (index.start != index.limit) {
- // Find the rollbackStart, adjusted for length changes
- // and the deletion of partially transliterated text.
- int32_t rs = rollbackStart + delta - (index.limit - passStart);
- // Delete the partially transliterated text
- text.handleReplaceBetween(passStart, index.limit, UnicodeString());
- // Copy the rollback text back
- text.copy(rs, rs + uncommittedLength, passStart);
- // Restore indices to their original values
- index.start = passStart;
- index.limit = passLimit;
- index.contextLimit -= delta;
- }
- // We did completely transliterate this pass. Update the
- // commit indices to record how far we got. Adjust indices
- // for length change.
- else {
- // Move the pass indices past the committed text.
- passStart = passLimit = index.start;
- // Adjust the rollbackStart for length changes and move
- // it past the committed text. All characters we've
- // processed to this point are committed now, so zero
- // out the uncommittedLength.
- rollbackStart += delta + uncommittedLength;
- uncommittedLength = 0;
- // Adjust indices for length changes.
- runLimit += delta;
- totalDelta += delta;
- }
- }
- // Adjust overall limit and rollbackOrigin for insertions and
- // deletions. Don't need to worry about contextLimit because
- // handleTransliterate() maintains that.
- rollbackOrigin += totalDelta;
- globalLimit += totalDelta;
- // Delete the rollback copy
- text.handleReplaceBetween(rollbackOrigin, rollbackOrigin + runLength, UnicodeString());
- // Move start past committed text
- index.start = passStart;
- }
- else {
- // Delegate to subclass for actual transliteration.
- int32_t limit = index.limit;
- handleTransliterate(text, index, isIncrementalRun);
- delta = index.limit - limit; // change in length
- // In a properly written transliterator, start == limit after
- // handleTransliterate() returns when incremental is false.
- // Catch cases where the subclass doesn't do this, and throw
- // an exception. (Just pinning start to limit is a bad idea,
- // because what's probably happening is that the subclass
- // isn't transliterating all the way to the end, and it should
- // in non-incremental mode.)
- if (!incremental && index.start != index.limit) {
- // We can't throw an exception, so just fudge things
- index.start = index.limit;
- }
- // Adjust overall limit for insertions/deletions. Don't need
- // to worry about contextLimit because handleTransliterate()
- // maintains that.
- globalLimit += delta;
- }
- if (filter == nullptr || isIncrementalRun) {
- break;
- }
- // If we did completely transliterate this
- // run, then repeat with the next unfiltered run.
- }
- // Start is valid where it is. Limit needs to be put back where
- // it was, modulo adjustments for deletions/insertions.
- index.limit = globalLimit;
- }
- void Transliterator::filteredTransliterate(Replaceable& text,
- UTransPosition& index,
- UBool incremental) const {
- filteredTransliterate(text, index, incremental, false);
- }
- /**
- * Method for subclasses to use to set the maximum context length.
- * @see #getMaximumContextLength
- */
- void Transliterator::setMaximumContextLength(int32_t maxContextLength) {
- maximumContextLength = maxContextLength;
- }
- /**
- * Returns a programmatic identifier for this transliterator.
- * If this identifier is passed to <code>getInstance()</code>, it
- * will return this object, if it has been registered.
- * @see #registerInstance
- * @see #getAvailableIDs
- */
- const UnicodeString& Transliterator::getID() 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
- * <code>java.text</code> package.
- *
- * <p>If no localized names exist in the system resource bundles,
- * a name is synthesized using a localized
- * <code>MessageFormat</code> 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 <tt>null</tt>
- * if this transliterator uses no filter. Caller musn't delete
- * the result!
- */
- const UnicodeFilter* Transliterator::getFilter() const {
- return filter;
- }
- /**
- * Returns the filter used by this transliterator, or
- * <tt>nullptr</tt> if this transliterator uses no filter. The
- * caller must eventually delete the result. After this call,
- * this transliterator's filter is set to <tt>nullptr</tt>.
- */
- UnicodeFilter* Transliterator::orphanFilter() {
- UnicodeFilter *result = filter;
- filter = nullptr;
- return result;
- }
- /**
- * Changes the filter used by this transliterator. If the filter
- * is set to <tt>null</tt> then no filtering will occur.
- *
- * <p>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 <code>getID()</code>
- * returns "A-B", then this method will return the result of
- * <code>getInstance("B-A")</code>, or <code>null</code> if that
- * call fails.
- *
- * <p>This method does not take filtering into account. The
- * returned transliterator will have no filter.
- *
- * <p>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 <code>null</code> 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 <code>Transliterator</code> object given its ID.
- * The ID must be either a system transliterator ID or a ID registered
- * using <code>registerInstance()</code>.
- *
- * @param ID a valid ID, as enumerated by <code>getAvailableIDs()</code>
- * @return A <code>Transliterator</code> 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 nullptr;
- }
- UnicodeSet* globalFilter = nullptr;
- // 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;
- delete globalFilter;
- return nullptr;
- }
- LocalPointer<UnicodeSet> lpGlobalFilter(globalFilter);
-
- TransliteratorIDParser::instantiateList(list, status);
- if (U_FAILURE(status)) {
- return nullptr;
- }
-
- U_ASSERT(list.size() > 0);
- Transliterator* t = nullptr;
-
- 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 != nullptr) {
- t->setID(canonID);
- if (lpGlobalFilter.isValid()) {
- t->adoptFilter(lpGlobalFilter.orphan());
- }
- }
- 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 = nullptr;
- break;
- }
- }
- if (t != nullptr && canon != nullptr) {
- t->setID(*canon);
- }
- return t;
- }
- /**
- * Returns a <code>Transliterator</code> 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 = nullptr;
- 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 != nullptr) {
- 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 != nullptr) {
- t->setID(ID);
- }
- }
- else {
- UVector transliterators(status);
- // TODO ICU-21701 missing U_FAILURE check here.
- // Error and nullptr checking through this whole block looks suspect.
- 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 (U_FAILURE(status)) {
- delete temp;
- return nullptr;
- }
- if (temp != nullptr && typeid(*temp) != typeid(NullTransliterator)) {
- transliterators.addElement(temp, status);
- if (U_FAILURE(status)) {
- delete temp;
- return nullptr;
- }
- } 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 nullptr before adding it to transliterators to avoid future usage of nullptr pointer.
- if (temprbt == nullptr) {
- if (U_SUCCESS(status)) {
- status = U_MEMORY_ALLOCATION_ERROR;
- }
- return t;
- }
- transliterators.addElement(temprbt, status);
- if (U_FAILURE(status)) {
- delete temprbt;
- return t;
- }
- // TODO: ICU-21701 the transliterators vector will leak its contents if anything goes wrong.
- // Under normal operation, the CompoundTransliterator constructor adopts the
- // the contents of the vector.
- }
- }
- t = new CompoundTransliterator(transliterators, passNumber - 1, parseError, status);
- // Null pointer check
- if (t != nullptr) {
- t->setID(ID);
- t->adoptFilter(parser.orphanCompoundFilter());
- }
- }
- if (U_SUCCESS(status) && t == nullptr) {
- 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; i<id.length();) {
- UChar32 c = id.char32At(i);
- if (!ICU_Utility::escapeUnprintable(rulesSource, c)) {
- rulesSource.append(c);
- }
- i += U16_LENGTH(c);
- }
- } else {
- rulesSource = getID();
- }
- // KEEP in sync with rbt_pars
- rulesSource.insert(0, UNICODE_STRING_SIMPLE("::"));
- rulesSource.append(ID_DELIM);
- return rulesSource;
- }
- int32_t Transliterator::countElements() const {
- const CompoundTransliterator* ct = dynamic_cast<const CompoundTransliterator*>(this);
- return ct != nullptr ? ct->getCount() : 0;
- }
- const Transliterator& Transliterator::getElement(int32_t index, UErrorCode& ec) const {
- if (U_FAILURE(ec)) {
- return *this;
- }
- const CompoundTransliterator* cpd = dynamic_cast<const CompoundTransliterator*>(this);
- int32_t n = (cpd == nullptr) ? 1 : cpd->getCount();
- if (index < 0 || index >= n) {
- ec = U_INDEX_OUTOFBOUNDS_ERROR;
- return *this;
- } else {
- return (n == 1) ? *this : cpd->getTransliterator(index);
- }
- }
- UnicodeSet& Transliterator::getSourceSet(UnicodeSet& result) const {
- handleGetSourceSet(result);
- if (filter != nullptr) {
- UnicodeSet* filterSet = dynamic_cast<UnicodeSet*>(filter);
- UBool deleteFilterSet = false;
- // Most, but not all filters will be UnicodeSets. Optimize for
- // the high-runner case.
- if (filterSet == nullptr) {
- filterSet = new UnicodeSet();
- // Check null pointer
- if (filterSet == nullptr) {
- return result;
- }
- deleteFilterSet = true;
- filter->addMatchSetTo(*filterSet);
- }
- result.retainAll(*filterSet);
- if (deleteFilterSet) {
- delete filterSet;
- }
- }
- return result;
- }
- void Transliterator::handleGetSourceSet(UnicodeSet& result) const {
- result.clear();
- }
- UnicodeSet& Transliterator::getTargetSet(UnicodeSet& result) const {
- return result.clear();
- }
- // For public consumption
- void U_EXPORT2 Transliterator::registerFactory(const UnicodeString& id,
- Transliterator::Factory factory,
- Transliterator::Token context) {
- Mutex lock(®istryMutex);
- UErrorCode ec = U_ZERO_ERROR;
- if (HAVE_REGISTRY(ec)) {
- _registerFactory(id, factory, context);
- }
- }
- // To be called only by Transliterator subclasses that are called
- // to register themselves by initializeRegistry().
- void Transliterator::_registerFactory(const UnicodeString& id,
- Transliterator::Factory factory,
- Transliterator::Token context) {
- UErrorCode ec = U_ZERO_ERROR;
- registry->put(id, factory, context, true, ec);
- }
- // To be called only by Transliterator subclasses that are called
- // to register themselves by initializeRegistry().
- void Transliterator::_registerSpecialInverse(const UnicodeString& target,
- const UnicodeString& inverseTarget,
- UBool bidirectional) {
- UErrorCode status = U_ZERO_ERROR;
- TransliteratorIDParser::registerSpecialInverse(target, inverseTarget, bidirectional, status);
- }
- /**
- * Registers a instance <tt>obj</tt> of a subclass of
- * <code>Transliterator</code> with the system. This object must
- * implement the <tt>clone()</tt> method. When
- * <tt>getInstance()</tt> is called with an ID string that is
- * equal to <tt>obj.getID()</tt>, then <tt>obj.clone()</tt> is
- * returned.
- *
- * @param obj an instance of subclass of
- * <code>Transliterator</code> that defines <tt>clone()</tt>
- * @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() {
- 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 = nullptr;
- umtx_lock(®istryMutex);
- UErrorCode ec = U_ZERO_ERROR;
- if (HAVE_REGISTRY(ec)) {
- result = ®istry->getAvailableID(index);
- }
- umtx_unlock(®istryMutex);
- U_ASSERT(result != nullptr); // fail if no registry
- return *result;
- }
- StringEnumeration* U_EXPORT2 Transliterator::getAvailableIDs(UErrorCode& ec) {
- if (U_FAILURE(ec)) return nullptr;
- StringEnumeration* result = nullptr;
- umtx_lock(®istryMutex);
- if (HAVE_REGISTRY(ec)) {
- result = registry->getAvailableIDs();
- }
- umtx_unlock(®istryMutex);
- if (result == nullptr) {
- ec = U_INTERNAL_TRANSLITERATOR_ERROR;
- }
- return result;
- }
- int32_t U_EXPORT2 Transliterator::countAvailableSources() {
- 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() {
- 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.
- */
- char16_t Transliterator::filteredCharAt(const Replaceable& text, int32_t i) const {
- char16_t c;
- const UnicodeFilter* localFilter = getFilter();
- return (localFilter == 0) ? text.charAt(i) :
- (localFilter->contains(c = text.charAt(i)) ? c : (char16_t)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:
- * <id>{
- * file{
- * resource{"<resource>"}
- * direction{"<direction>"}
- * }
- * }
- * <id>{
- * internal{
- * resource{"<resource>"}
- * direction{"<direction"}
- * }
- * }
- * <id>{
- * alias{"<getInstanceArg"}
- * }
- * <id> is the ID of the system transliterator being defined. These
- * are public IDs enumerated by Transliterator.getAvailableIDs(),
- * unless the second field is "internal".
- *
- * <resource> is a ResourceReader resource name. Currently these refer
- * to file names under com/ibm/text/resources. This string is passed
- * directly to ResourceReader, together with <encoding>.
- *
- * <direction> is either "FORWARD" or "REVERSE".
- *
- * <getInstanceArg> is a string to be passed directly to
- * Transliterator.getInstance(). The returned Transliterator object
- * then has its ID changed to <id> and is returned.
- *
- * The extra blank field on "alias" lines is to make the array square.
- */
- //static const char translit_index[] = "translit_index";
- UErrorCode lstatus = U_ZERO_ERROR;
- UResourceBundle *bundle, *transIDs, *colBund;
- bundle = ures_open(U_ICUDATA_TRANSLIT, nullptr/*open default locale*/, &lstatus);
- transIDs = ures_getByKey(bundle, RB_RULE_BASED_IDS, 0, &lstatus);
- const UnicodeString T_PART = UNICODE_STRING_SIMPLE("-t-");
- int32_t row, maxRows;
- if (lstatus == U_MEMORY_ALLOCATION_ERROR) {
- delete registry;
- registry = nullptr;
- status = U_MEMORY_ALLOCATION_ERROR;
- return false;
- }
- if (U_SUCCESS(lstatus)) {
- maxRows = ures_getSize(transIDs);
- for (row = 0; row < maxRows; row++) {
- colBund = ures_getByIndex(transIDs, row, 0, &lstatus);
- if (U_SUCCESS(lstatus)) {
- UnicodeString id(ures_getKey(colBund), -1, US_INV);
- if(id.indexOf(T_PART) != -1) {
- ures_close(colBund);
- continue;
- }
- UResourceBundle* res = ures_getNextResource(colBund, nullptr, &lstatus);
- const char* typeStr = ures_getKey(res);
- char16_t type;
- u_charsToUChars(typeStr, &type, 1);
- if (U_SUCCESS(lstatus)) {
- int32_t len = 0;
- const char16_t *resString;
- switch (type) {
- case 0x66: // 'f'
- case 0x69: // 'i'
- // 'file' or 'internal';
- // row[2]=resource, row[3]=direction
- {
-
- resString = ures_getStringByKey(res, "resource", &len, &lstatus);
- UBool visible = (type == 0x0066 /*f*/);
- UTransDirection dir =
- (ures_getUnicodeStringByKey(res, "direction", &lstatus).charAt(0) ==
- 0x0046 /*F*/) ?
- UTRANS_FORWARD : UTRANS_REVERSE;
- registry->put(id, UnicodeString(true, resString, len), dir, true, visible, lstatus);
- }
- break;
- case 0x61: // 'a'
- // 'alias'; row[2]=createInstance argument
- resString = ures_getString(res, &len, &lstatus);
- registry->put(id, UnicodeString(true, resString, len), true, true, lstatus);
- break;
- }
- }
- ures_close(res);
- }
- ures_close(colBund);
- }
- }
- ures_close(transIDs);
- ures_close(bundle);
- // Manually add prototypes that the system knows about to the
- // cache. This is how new non-rule-based transliterators are
- // added to the system.
-
- // This is to allow for null pointer check
- NullTransliterator* tempNullTranslit = new NullTransliterator();
- LowercaseTransliterator* tempLowercaseTranslit = new LowercaseTransliterator();
- UppercaseTransliterator* tempUppercaseTranslit = new UppercaseTransliterator();
- TitlecaseTransliterator* tempTitlecaseTranslit = new TitlecaseTransliterator();
- UnicodeNameTransliterator* tempUnicodeTranslit = new UnicodeNameTransliterator();
- NameUnicodeTransliterator* tempNameUnicodeTranslit = new NameUnicodeTransliterator();
- #if !UCONFIG_NO_BREAK_ITERATION
- // TODO: could or should these transliterators be referenced polymorphically once constructed?
- BreakTransliterator* tempBreakTranslit = new BreakTransliterator();
- #endif
- // Check for null pointers
- if (tempNullTranslit == nullptr || tempLowercaseTranslit == nullptr || tempUppercaseTranslit == nullptr ||
- tempTitlecaseTranslit == nullptr || tempUnicodeTranslit == nullptr ||
- #if !UCONFIG_NO_BREAK_ITERATION
- tempBreakTranslit == nullptr ||
- #endif
- tempNameUnicodeTranslit == nullptr )
- {
- delete tempNullTranslit;
- delete tempLowercaseTranslit;
- delete tempUppercaseTranslit;
- delete tempTitlecaseTranslit;
- delete tempUnicodeTranslit;
- delete tempNameUnicodeTranslit;
- #if !UCONFIG_NO_BREAK_ITERATION
- delete tempBreakTranslit;
- #endif
- // Since there was an error, remove registry
- delete registry;
- registry = nullptr;
- status = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- registry->put(tempNullTranslit, true, status);
- registry->put(tempLowercaseTranslit, true, status);
- registry->put(tempUppercaseTranslit, true, status);
- registry->put(tempTitlecaseTranslit, true, status);
- registry->put(tempUnicodeTranslit, true, status);
- registry->put(tempNameUnicodeTranslit, true, status);
- #if !UCONFIG_NO_BREAK_ITERATION
- registry->put(tempBreakTranslit, false, status); // false means invisible.
- #endif
- RemoveTransliterator::registerIDs(); // Must be within mutex
- EscapeTransliterator::registerIDs();
- UnescapeTransliterator::registerIDs();
- NormalizationTransliterator::registerIDs();
- AnyTransliterator::registerIDs();
- _registerSpecialInverse(UNICODE_STRING_SIMPLE("Null"),
- UNICODE_STRING_SIMPLE("Null"), false);
- _registerSpecialInverse(UNICODE_STRING_SIMPLE("Upper"),
- UNICODE_STRING_SIMPLE("Lower"), true);
- _registerSpecialInverse(UNICODE_STRING_SIMPLE("Title"),
- UNICODE_STRING_SIMPLE("Lower"), false);
- ucln_i18n_registerCleanup(UCLN_I18N_TRANSLITERATOR, utrans_transliterator_cleanup);
- return true;
- }
- U_NAMESPACE_END
- // Defined in transreg.h:
- /**
- * Release all static memory held by transliterator. This will
- * necessarily invalidate any rule-based transliterators held by the
- * user, because RBTs hold pointers to common data objects.
- */
- U_CFUNC UBool utrans_transliterator_cleanup() {
- U_NAMESPACE_USE
- TransliteratorIDParser::cleanup();
- if (registry) {
- delete registry;
- registry = nullptr;
- }
- return true;
- }
- #endif /* #if !UCONFIG_NO_TRANSLITERATION */
- //eof
|