1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 **********************************************************************
5 * Copyright (c) 2002-2014, International Business Machines Corporation
6 * and others. All Rights Reserved.
7 **********************************************************************
8 * Date Name Description
9 * 01/14/2002 aliu Creation.
10 **********************************************************************
13 #include "unicode/utypes.h"
15 #if !UCONFIG_NO_TRANSLITERATION
23 #include "unicode/parsepos.h"
24 #include "unicode/translit.h"
25 #include "unicode/uchar.h"
26 #include "unicode/uniset.h"
27 #include "unicode/unistr.h"
28 #include "unicode/utrans.h"
34 static const UChar ID_DELIM
= 0x003B; // ;
35 static const UChar TARGET_SEP
= 0x002D; // -
36 static const UChar VARIANT_SEP
= 0x002F; // /
37 static const UChar OPEN_REV
= 0x0028; // (
38 static const UChar CLOSE_REV
= 0x0029; // )
40 //static const UChar EMPTY[] = {0}; // ""
41 static const UChar ANY
[] = {65,110,121,0}; // "Any"
42 static const UChar ANY_NULL
[] = {65,110,121,45,78,117,108,108,0}; // "Any-Null"
44 static const int32_t FORWARD
= UTRANS_FORWARD
;
45 static const int32_t REVERSE
= UTRANS_REVERSE
;
47 static Hashtable
* SPECIAL_INVERSES
= NULL
;
48 static UInitOnce gSpecialInversesInitOnce
= U_INITONCE_INITIALIZER
;
51 * The mutex controlling access to SPECIAL_INVERSES
53 static UMutex
*LOCK() {
54 static UMutex
*m
= STATIC_NEW(UMutex
);
58 TransliteratorIDParser::Specs::Specs(const UnicodeString
& s
, const UnicodeString
& t
,
59 const UnicodeString
& v
, UBool sawS
,
60 const UnicodeString
& f
) {
68 TransliteratorIDParser::SingleID::SingleID(const UnicodeString
& c
, const UnicodeString
& b
,
69 const UnicodeString
& f
) {
75 TransliteratorIDParser::SingleID::SingleID(const UnicodeString
& c
, const UnicodeString
& b
) {
80 Transliterator
* TransliteratorIDParser::SingleID::createInstance() {
82 if (basicID
.length() == 0) {
83 t
= createBasicInstance(UnicodeString(TRUE
, ANY_NULL
, 8), &canonID
);
85 t
= createBasicInstance(basicID
, &canonID
);
88 if (filter
.length() != 0) {
89 UErrorCode ec
= U_ZERO_ERROR
;
90 UnicodeSet
*set
= new UnicodeSet(filter
, ec
);
103 * Parse a single ID, that is, an ID of the general form
104 * "[f1] s1-t1/v1 ([f2] s2-t3/v2)", with the parenthesized element
105 * optional, the filters optional, and the variants optional.
106 * @param id the id to be parsed
107 * @param pos INPUT-OUTPUT parameter. On input, the position of
108 * the first character to parse. On output, the position after
109 * the last character parsed.
110 * @param dir the direction. If the direction is REVERSE then the
111 * SingleID is constructed for the reverse direction.
112 * @return a SingleID object or NULL
114 TransliteratorIDParser::SingleID
*
115 TransliteratorIDParser::parseSingleID(const UnicodeString
& id
, int32_t& pos
,
116 int32_t dir
, UErrorCode
& status
) {
120 // The ID will be of the form A, A(), A(B), or (B), where
121 // A and B are filter IDs.
122 Specs
* specsA
= NULL
;
123 Specs
* specsB
= NULL
;
124 UBool sawParen
= FALSE
;
126 // On the first pass, look for (B) or (). If this fails, then
127 // on the second pass, look for A, A(B), or A().
128 for (int32_t pass
=1; pass
<=2; ++pass
) {
130 specsA
= parseFilterID(id
, pos
, TRUE
);
131 if (specsA
== NULL
) {
136 if (ICU_Utility::parseChar(id
, pos
, OPEN_REV
)) {
138 if (!ICU_Utility::parseChar(id
, pos
, CLOSE_REV
)) {
139 specsB
= parseFilterID(id
, pos
, TRUE
);
140 // Must close with a ')'
141 if (specsB
== NULL
|| !ICU_Utility::parseChar(id
, pos
, CLOSE_REV
)) {
151 // Assemble return results
154 if (dir
== FORWARD
) {
155 SingleID
* b
= specsToID(specsB
, FORWARD
);
156 single
= specsToID(specsA
, FORWARD
);
157 // Null pointers check
158 if (b
== NULL
|| single
== NULL
) {
161 status
= U_MEMORY_ALLOCATION_ERROR
;
164 single
->canonID
.append(OPEN_REV
)
165 .append(b
->canonID
).append(CLOSE_REV
);
166 if (specsA
!= NULL
) {
167 single
->filter
= specsA
->filter
;
171 SingleID
* a
= specsToID(specsA
, FORWARD
);
172 single
= specsToID(specsB
, FORWARD
);
173 // Check for null pointer.
174 if (a
== NULL
|| single
== NULL
) {
177 status
= U_MEMORY_ALLOCATION_ERROR
;
180 single
->canonID
.append(OPEN_REV
)
181 .append(a
->canonID
).append(CLOSE_REV
);
182 if (specsB
!= NULL
) {
183 single
->filter
= specsB
->filter
;
188 // assert(specsA != NULL);
189 if (dir
== FORWARD
) {
190 single
= specsToID(specsA
, FORWARD
);
192 single
= specsToSpecialInverse(*specsA
, status
);
193 if (single
== NULL
) {
194 single
= specsToID(specsA
, REVERSE
);
197 // Check for NULL pointer
198 if (single
== NULL
) {
199 status
= U_MEMORY_ALLOCATION_ERROR
;
202 single
->filter
= specsA
->filter
;
212 * Parse a filter ID, that is, an ID of the general form
213 * "[f1] s1-t1/v1", with the filters optional, and the variants optional.
214 * @param id the id to be parsed
215 * @param pos INPUT-OUTPUT parameter. On input, the position of
216 * the first character to parse. On output, the position after
217 * the last character parsed.
218 * @return a SingleID object or null if the parse fails
220 TransliteratorIDParser::SingleID
*
221 TransliteratorIDParser::parseFilterID(const UnicodeString
& id
, int32_t& pos
) {
225 Specs
* specs
= parseFilterID(id
, pos
, TRUE
);
231 // Assemble return results
232 SingleID
* single
= specsToID(specs
, FORWARD
);
233 if (single
!= NULL
) {
234 single
->filter
= specs
->filter
;
241 * Parse a global filter of the form "[f]" or "([f])", depending
243 * @param id the pattern the parse
244 * @param pos INPUT-OUTPUT parameter. On input, the position of
245 * the first character to parse. On output, the position after
246 * the last character parsed.
247 * @param dir the direction.
248 * @param withParens INPUT-OUTPUT parameter. On entry, if
249 * withParens is 0, then parens are disallowed. If it is 1,
250 * then parens are requires. If it is -1, then parens are
251 * optional, and the return result will be set to 0 or 1.
252 * @param canonID OUTPUT parameter. The pattern for the filter
253 * added to the canonID, either at the end, if dir is FORWARD, or
254 * at the start, if dir is REVERSE. The pattern will be enclosed
255 * in parentheses if appropriate, and will be suffixed with an
256 * ID_DELIM character. May be NULL.
257 * @return a UnicodeSet object or NULL. A non-NULL results
258 * indicates a successful parse, regardless of whether the filter
259 * applies to the given direction. The caller should discard it
260 * if withParens != (dir == REVERSE).
262 UnicodeSet
* TransliteratorIDParser::parseGlobalFilter(const UnicodeString
& id
, int32_t& pos
,
265 UnicodeString
* canonID
) {
266 UnicodeSet
* filter
= NULL
;
269 if (withParens
== -1) {
270 withParens
= ICU_Utility::parseChar(id
, pos
, OPEN_REV
) ? 1 : 0;
271 } else if (withParens
== 1) {
272 if (!ICU_Utility::parseChar(id
, pos
, OPEN_REV
)) {
278 ICU_Utility::skipWhitespace(id
, pos
, TRUE
);
280 if (UnicodeSet::resemblesPattern(id
, pos
)) {
281 ParsePosition
ppos(pos
);
282 UErrorCode ec
= U_ZERO_ERROR
;
283 filter
= new UnicodeSet(id
, ppos
, USET_IGNORE_SPACE
, NULL
, ec
);
295 UnicodeString pattern
;
296 id
.extractBetween(pos
, ppos
.getIndex(), pattern
);
297 pos
= ppos
.getIndex();
299 if (withParens
== 1 && !ICU_Utility::parseChar(id
, pos
, CLOSE_REV
)) {
304 // In the forward direction, append the pattern to the
305 // canonID. In the reverse, insert it at zero, and invert
306 // the presence of parens ("A" <-> "(A)").
307 if (canonID
!= NULL
) {
308 if (dir
== FORWARD
) {
309 if (withParens
== 1) {
310 pattern
.insert(0, OPEN_REV
);
311 pattern
.append(CLOSE_REV
);
313 canonID
->append(pattern
).append(ID_DELIM
);
315 if (withParens
== 0) {
316 pattern
.insert(0, OPEN_REV
);
317 pattern
.append(CLOSE_REV
);
319 canonID
->insert(0, pattern
);
320 canonID
->insert(pattern
.length(), ID_DELIM
);
329 static void U_CALLCONV
_deleteSingleID(void* obj
) {
330 delete (TransliteratorIDParser::SingleID
*) obj
;
333 static void U_CALLCONV
_deleteTransliteratorTrIDPars(void* obj
) {
334 delete (Transliterator
*) obj
;
339 * Parse a compound ID, consisting of an optional forward global
340 * filter, a separator, one or more single IDs delimited by
341 * separators, an an optional reverse global filter. The
342 * separator is a semicolon. The global filters are UnicodeSet
343 * patterns. The reverse global filter must be enclosed in
345 * @param id the pattern the parse
346 * @param dir the direction.
347 * @param canonID OUTPUT parameter that receives the canonical ID,
348 * consisting of canonical IDs for all elements, as returned by
349 * parseSingleID(), separated by semicolons. Previous contents
351 * @param list OUTPUT parameter that receives a list of SingleID
352 * objects representing the parsed IDs. Previous contents are
354 * @param globalFilter OUTPUT parameter that receives a pointer to
355 * a newly created global filter for this ID in this direction, or
356 * NULL if there is none.
357 * @return TRUE if the parse succeeds, that is, if the entire
358 * id is consumed without syntax error.
360 UBool
TransliteratorIDParser::parseCompoundID(const UnicodeString
& id
, int32_t dir
,
361 UnicodeString
& canonID
,
363 UnicodeSet
*& globalFilter
) {
364 UErrorCode ec
= U_ZERO_ERROR
;
367 int32_t withParens
= 1;
368 list
.removeAllElements();
373 // Parse leading global filter, if any
374 withParens
= 0; // parens disallowed
375 filter
= parseGlobalFilter(id
, pos
, dir
, withParens
, &canonID
);
376 if (filter
!= NULL
) {
377 if (!ICU_Utility::parseChar(id
, pos
, ID_DELIM
)) {
378 // Not a global filter; backup and resume
382 if (dir
== FORWARD
) {
383 globalFilter
= filter
;
390 UBool sawDelimiter
= TRUE
;
392 SingleID
* single
= parseSingleID(id
, pos
, dir
, ec
);
393 if (single
== NULL
) {
396 if (dir
== FORWARD
) {
397 list
.addElement(single
, ec
);
399 list
.insertElementAt(single
, 0, ec
);
404 if (!ICU_Utility::parseChar(id
, pos
, ID_DELIM
)) {
405 sawDelimiter
= FALSE
;
410 if (list
.size() == 0) {
414 // Construct canonical ID
415 for (i
=0; i
<list
.size(); ++i
) {
416 SingleID
* single
= (SingleID
*) list
.elementAt(i
);
417 canonID
.append(single
->canonID
);
418 if (i
!= (list
.size()-1)) {
419 canonID
.append(ID_DELIM
);
423 // Parse trailing global filter, if any, and only if we saw
424 // a trailing delimiter after the IDs.
426 withParens
= 1; // parens required
427 filter
= parseGlobalFilter(id
, pos
, dir
, withParens
, &canonID
);
428 if (filter
!= NULL
) {
429 // Don't require trailing ';', but parse it if present
430 ICU_Utility::parseChar(id
, pos
, ID_DELIM
);
432 if (dir
== REVERSE
) {
433 globalFilter
= filter
;
441 // Trailing unparsed text is a syntax error
442 ICU_Utility::skipWhitespace(id
, pos
, TRUE
);
443 if (pos
!= id
.length()) {
450 UObjectDeleter
*save
= list
.setDeleter(_deleteSingleID
);
451 list
.removeAllElements();
452 list
.setDeleter(save
);
459 * Convert the elements of the 'list' vector, which are SingleID
460 * objects, into actual Transliterator objects. In the course of
461 * this, some (or all) entries may be removed. If all entries
462 * are removed, the NULL transliterator will be added.
464 * Delete entries with empty basicIDs; these are generated by
465 * elements like "(A)" in the forward direction, or "A()" in
466 * the reverse. THIS MAY RESULT IN AN EMPTY VECTOR. Convert
467 * SingleID entries to actual transliterators.
469 * @param list vector of SingleID objects. On exit, vector
470 * of one or more Transliterators.
471 * @return new value of insertIndex. The index will shift if
472 * there are empty items, like "(Lower)", with indices less than
475 void TransliteratorIDParser::instantiateList(UVector
& list
,
481 tlist
.setDeleter(_deleteTransliteratorTrIDPars
);
485 for (i
=0; i
<=list
.size(); ++i
) { // [sic]: i<=list.size()
486 // We run the loop too long by one, so we can
487 // do an insert after the last element
488 if (i
==list
.size()) {
492 SingleID
* single
= (SingleID
*) list
.elementAt(i
);
493 if (single
->basicID
.length() != 0) {
494 t
= single
->createInstance();
499 tlist
.addElement(t
, ec
);
507 // An empty list is equivalent to a NULL transliterator.
508 if (tlist
.size() == 0) {
509 t
= createBasicInstance(UnicodeString(TRUE
, ANY_NULL
, 8), NULL
);
511 // Should never happen
512 ec
= U_INTERNAL_TRANSLITERATOR_ERROR
;
514 tlist
.addElement(t
, ec
);
522 UObjectDeleter
*save
= list
.setDeleter(_deleteSingleID
);
523 list
.removeAllElements();
526 list
.setDeleter(_deleteTransliteratorTrIDPars
);
528 while (tlist
.size() > 0) {
529 t
= (Transliterator
*) tlist
.orphanElementAt(0);
530 list
.addElement(t
, ec
);
533 list
.removeAllElements();
539 list
.setDeleter(save
);
543 * Parse an ID into pieces. Take IDs of the form T, T/V, S-T,
544 * S-T/V, or S/V-T. If the source is missing, return a source of
546 * @param id the id string, in any of several forms
547 * @return an array of 4 strings: source, target, variant, and
548 * isSourcePresent. If the source is not present, ANY will be
549 * given as the source, and isSourcePresent will be NULL. Otherwise
550 * isSourcePresent will be non-NULL. The target may be empty if the
551 * id is not well-formed. The variant may be empty.
553 void TransliteratorIDParser::IDtoSTV(const UnicodeString
& id
,
554 UnicodeString
& source
,
555 UnicodeString
& target
,
556 UnicodeString
& variant
,
557 UBool
& isSourcePresent
) {
558 source
.setTo(ANY
, 3);
562 int32_t sep
= id
.indexOf(TARGET_SEP
);
563 int32_t var
= id
.indexOf(VARIANT_SEP
);
567 isSourcePresent
= FALSE
;
570 // Form: T/V or T (or /V)
571 id
.extractBetween(0, var
, target
);
572 id
.extractBetween(var
, id
.length(), variant
);
573 } else if (sep
< var
) {
574 // Form: S-T/V or S-T (or -T/V or -T)
576 id
.extractBetween(0, sep
, source
);
577 isSourcePresent
= TRUE
;
579 id
.extractBetween(++sep
, var
, target
);
580 id
.extractBetween(var
, id
.length(), variant
);
582 // Form: (S/V-T or /V-T)
584 id
.extractBetween(0, var
, source
);
585 isSourcePresent
= TRUE
;
587 id
.extractBetween(var
, sep
++, variant
);
588 id
.extractBetween(sep
, id
.length(), target
);
591 if (variant
.length() > 0) {
592 variant
.remove(0, 1);
597 * Given source, target, and variant strings, concatenate them into a
598 * full ID. If the source is empty, then "Any" will be used for the
599 * source, so the ID will always be of the form s-t/v or s-t.
601 void TransliteratorIDParser::STVtoID(const UnicodeString
& source
,
602 const UnicodeString
& target
,
603 const UnicodeString
& variant
,
606 if (id
.length() == 0) {
609 id
.append(TARGET_SEP
).append(target
);
610 if (variant
.length() != 0) {
611 id
.append(VARIANT_SEP
).append(variant
);
613 // NUL-terminate the ID string for getTerminatedBuffer.
614 // This prevents valgrind and Purify warnings.
616 id
.truncate(id
.length()-1);
620 * Register two targets as being inverses of one another. For
621 * example, calling registerSpecialInverse("NFC", "NFD", TRUE) causes
622 * Transliterator to form the following inverse relationships:
627 * Any-NFD => Any-NFC</pre>
629 * (Without the special inverse registration, the inverse of NFC
630 * would be NFC-Any.) Note that NFD is shorthand for Any-NFD, but
631 * that the presence or absence of "Any-" is preserved.
633 * <p>The relationship is symmetrical; registering (a, b) is
634 * equivalent to registering (b, a).
636 * <p>The relevant IDs must still be registered separately as
637 * factories or classes.
639 * <p>Only the targets are specified. Special inverses always
640 * have the form Any-Target1 <=> Any-Target2. The target should
641 * have canonical casing (the casing desired to be produced when
642 * an inverse is formed) and should contain no whitespace or other
643 * extraneous characters.
645 * @param target the target against which to register the inverse
646 * @param inverseTarget the inverse of target, that is
647 * Any-target.getInverse() => Any-inverseTarget
648 * @param bidirectional if TRUE, register the reverse relation
649 * as well, that is, Any-inverseTarget.getInverse() => Any-target
651 void TransliteratorIDParser::registerSpecialInverse(const UnicodeString
& target
,
652 const UnicodeString
& inverseTarget
,
654 UErrorCode
&status
) {
655 umtx_initOnce(gSpecialInversesInitOnce
, init
, status
);
656 if (U_FAILURE(status
)) {
660 // If target == inverseTarget then force bidirectional => FALSE
661 if (bidirectional
&& 0==target
.caseCompare(inverseTarget
, U_FOLD_CASE_DEFAULT
)) {
662 bidirectional
= FALSE
;
667 UnicodeString
*tempus
= new UnicodeString(inverseTarget
); // Used for null pointer check before usage.
668 if (tempus
== NULL
) {
669 status
= U_MEMORY_ALLOCATION_ERROR
;
672 SPECIAL_INVERSES
->put(target
, tempus
, status
);
674 tempus
= new UnicodeString(target
);
675 if (tempus
== NULL
) {
676 status
= U_MEMORY_ALLOCATION_ERROR
;
679 SPECIAL_INVERSES
->put(inverseTarget
, tempus
, status
);
683 //----------------------------------------------------------------
684 // Private implementation
685 //----------------------------------------------------------------
688 * Parse an ID into component pieces. Take IDs of the form T,
689 * T/V, S-T, S-T/V, or S/V-T. If the source is missing, return a
691 * @param id the id string, in any of several forms
692 * @param pos INPUT-OUTPUT parameter. On input, pos is the
693 * offset of the first character to parse in id. On output,
694 * pos is the offset after the last parsed character. If the
695 * parse failed, pos will be unchanged.
696 * @param allowFilter2 if TRUE, a UnicodeSet pattern is allowed
697 * at any location between specs or delimiters, and is returned
698 * as the fifth string in the array.
699 * @return a Specs object, or NULL if the parse failed. If
700 * neither source nor target was seen in the parsed id, then the
701 * parse fails. If allowFilter is TRUE, then the parsed filter
702 * pattern is returned in the Specs object, otherwise the returned
703 * filter reference is NULL. If the parse fails for any reason
706 TransliteratorIDParser::Specs
*
707 TransliteratorIDParser::parseFilterID(const UnicodeString
& id
, int32_t& pos
,
710 UnicodeString source
;
711 UnicodeString target
;
712 UnicodeString variant
;
713 UnicodeString filter
;
715 int32_t specCount
= 0;
718 // This loop parses one of the following things with each
719 // pass: a filter, a delimiter character (either '-' or '/'),
720 // or a spec (source, target, or variant).
722 ICU_Utility::skipWhitespace(id
, pos
, TRUE
);
723 if (pos
== id
.length()) {
728 if (allowFilter
&& filter
.length() == 0 &&
729 UnicodeSet::resemblesPattern(id
, pos
)) {
731 ParsePosition
ppos(pos
);
732 UErrorCode ec
= U_ZERO_ERROR
;
733 UnicodeSet
set(id
, ppos
, USET_IGNORE_SPACE
, NULL
, ec
);
738 id
.extractBetween(pos
, ppos
.getIndex(), filter
);
739 pos
= ppos
.getIndex();
743 if (delimiter
== 0) {
744 UChar c
= id
.charAt(pos
);
745 if ((c
== TARGET_SEP
&& target
.length() == 0) ||
746 (c
== VARIANT_SEP
&& variant
.length() == 0)) {
753 // We are about to try to parse a spec with no delimiter
754 // when we can no longer do so (we can only do so at the
756 if (delimiter
== 0 && specCount
> 0) {
760 UnicodeString spec
= ICU_Utility::parseUnicodeIdentifier(id
, pos
);
761 if (spec
.length() == 0) {
762 // Note that if there was a trailing delimiter, we
763 // consume it. So Foo-, Foo/, Foo-Bar/, and Foo/Bar-
783 // A spec with no prior character is either source or target,
784 // depending on whether an explicit "-target" was seen.
785 if (first
.length() != 0) {
786 if (target
.length() == 0) {
793 // Must have either source or target
794 if (source
.length() == 0 && target
.length() == 0) {
799 // Empty source or target defaults to ANY
800 UBool sawSource
= TRUE
;
801 if (source
.length() == 0) {
802 source
.setTo(ANY
, 3);
805 if (target
.length() == 0) {
806 target
.setTo(ANY
, 3);
809 return new Specs(source
, target
, variant
, sawSource
, filter
);
813 * Givens a Spec object, convert it to a SingleID object. The
814 * Spec object is a more unprocessed parse result. The SingleID
815 * object contains information about canonical and basic IDs.
816 * @return a SingleID; never returns NULL. Returned object always
817 * has 'filter' field of NULL.
819 TransliteratorIDParser::SingleID
*
820 TransliteratorIDParser::specsToID(const Specs
* specs
, int32_t dir
) {
821 UnicodeString canonID
;
822 UnicodeString basicID
;
823 UnicodeString basicPrefix
;
826 if (dir
== FORWARD
) {
827 if (specs
->sawSource
) {
828 buf
.append(specs
->source
).append(TARGET_SEP
);
830 basicPrefix
= specs
->source
;
831 basicPrefix
.append(TARGET_SEP
);
833 buf
.append(specs
->target
);
835 buf
.append(specs
->target
).append(TARGET_SEP
).append(specs
->source
);
837 if (specs
->variant
.length() != 0) {
838 buf
.append(VARIANT_SEP
).append(specs
->variant
);
840 basicID
= basicPrefix
;
842 if (specs
->filter
.length() != 0) {
843 buf
.insert(0, specs
->filter
);
847 return new SingleID(canonID
, basicID
);
851 * Given a Specs object, return a SingleID representing the
852 * special inverse of that ID. If there is no special inverse
854 * @return a SingleID or NULL. Returned object always has
855 * 'filter' field of NULL.
857 TransliteratorIDParser::SingleID
*
858 TransliteratorIDParser::specsToSpecialInverse(const Specs
& specs
, UErrorCode
&status
) {
859 if (0!=specs
.source
.caseCompare(ANY
, 3, U_FOLD_CASE_DEFAULT
)) {
862 umtx_initOnce(gSpecialInversesInitOnce
, init
, status
);
863 if (U_FAILURE(status
)) {
867 UnicodeString
* inverseTarget
;
870 inverseTarget
= (UnicodeString
*) SPECIAL_INVERSES
->get(specs
.target
);
873 if (inverseTarget
!= NULL
) {
874 // If the original ID contained "Any-" then make the
875 // special inverse "Any-Foo"; otherwise make it "Foo".
876 // So "Any-NFC" => "Any-NFD" but "NFC" => "NFD".
878 if (specs
.filter
.length() != 0) {
879 buf
.append(specs
.filter
);
881 if (specs
.sawSource
) {
882 buf
.append(ANY
, 3).append(TARGET_SEP
);
884 buf
.append(*inverseTarget
);
886 UnicodeString
basicID(TRUE
, ANY
, 3);
887 basicID
.append(TARGET_SEP
).append(*inverseTarget
);
889 if (specs
.variant
.length() != 0) {
890 buf
.append(VARIANT_SEP
).append(specs
.variant
);
891 basicID
.append(VARIANT_SEP
).append(specs
.variant
);
893 return new SingleID(buf
, basicID
);
899 * Glue method to get around access problems in C++. This would
900 * ideally be inline but we want to avoid a circular header
903 Transliterator
* TransliteratorIDParser::createBasicInstance(const UnicodeString
& id
, const UnicodeString
* canonID
) {
904 return Transliterator::createBasicInstance(id
, canonID
);
908 * Initialize static memory. Called through umtx_initOnce only.
910 void U_CALLCONV
TransliteratorIDParser::init(UErrorCode
&status
) {
911 U_ASSERT(SPECIAL_INVERSES
== NULL
);
912 ucln_i18n_registerCleanup(UCLN_I18N_TRANSLITERATOR
, utrans_transliterator_cleanup
);
914 SPECIAL_INVERSES
= new Hashtable(TRUE
, status
);
915 if (SPECIAL_INVERSES
== NULL
) {
916 status
= U_MEMORY_ALLOCATION_ERROR
;
919 SPECIAL_INVERSES
->setValueDeleter(uprv_deleteUObject
);
923 * Free static memory.
925 void TransliteratorIDParser::cleanup() {
926 if (SPECIAL_INVERSES
) {
927 delete SPECIAL_INVERSES
;
928 SPECIAL_INVERSES
= NULL
;
930 gSpecialInversesInitOnce
.reset();
935 #endif /* #if !UCONFIG_NO_TRANSLITERATION */