2 **********************************************************************
\r
3 * Copyright (c) 2001-2009, International Business Machines
\r
4 * Corporation and others. All Rights Reserved.
\r
5 **********************************************************************
\r
6 * Date Name Description
\r
7 * 08/19/2001 aliu Creation.
\r
8 **********************************************************************
\r
11 package com.ibm.icu.text;
\r
13 //import com.ibm.icu.impl.ICULocaleData;
\r
14 import com.ibm.icu.impl.ICUResourceBundle;
\r
15 import com.ibm.icu.impl.LocaleUtility;
\r
16 import com.ibm.icu.lang.UScript;
\r
17 import com.ibm.icu.util.CaseInsensitiveString;
\r
18 import com.ibm.icu.util.UResourceBundle;
\r
20 import java.util.Enumeration;
\r
21 import java.util.Hashtable;
\r
22 import java.util.Locale;
\r
23 import java.util.MissingResourceException;
\r
24 import java.util.ResourceBundle;
\r
25 import java.util.Vector;
\r
27 class TransliteratorRegistry {
\r
30 private static final char LOCALE_SEP = '_';
\r
33 private static final String NO_VARIANT = ""; // empty string
\r
34 private static final String ANY = "Any";
\r
37 * Dynamic registry mapping full IDs to Entry objects. This
\r
38 * contains both public and internal entities. The visibility is
\r
39 * controlled by whether an entry is listed in availableIDs and
\r
42 * Keys are CaseInsensitiveString objects.
\r
43 * Values are objects of class Class (subclass of Transliterator),
\r
44 * RuleBasedTransliterator.Data, Transliterator.Factory, or one
\r
45 * of the entry classes defined here (AliasEntry or ResourceEntry).
\r
47 private Hashtable registry;
\r
50 * DAG of visible IDs by spec. Hashtable: source => (Hashtable:
\r
51 * target => (Vector: variant)) The Vector of variants is never
\r
52 * empty. For a source-target with no variant, the special
\r
53 * variant NO_VARIANT (the empty string) is stored in slot zero of
\r
56 * Keys are CaseInsensitiveString objects.
\r
57 * Values are Hashtable of (CaseInsensitiveString -> Vector of
\r
58 * CaseInsensitiveString)
\r
60 private Hashtable specDAG;
\r
63 * Vector of public full IDs (CaseInsensitiveString objects).
\r
65 private Vector availableIDs;
\r
67 //----------------------------------------------------------------------
\r
69 //----------------------------------------------------------------------
\r
72 * A Spec is a string specifying either a source or a target. In more
\r
73 * general terms, it may also specify a variant, but we only use the
\r
74 * Spec class for sources and targets.
\r
76 * A Spec may be a locale or a script. If it is a locale, it has a
\r
77 * fallback chain that goes xx_YY_ZZZ -> xx_YY -> xx -> ssss, where
\r
78 * ssss is the script mapping of xx_YY_ZZZ. The Spec API methods
\r
79 * hasFallback(), next(), and reset() iterate over this fallback
\r
82 * The Spec class canonicalizes itself, so the locale is put into
\r
83 * canonical form, or the script is transformed from an abbreviation
\r
88 private String top; // top spec
\r
89 private String spec; // current spec
\r
90 private String nextSpec; // next spec
\r
91 private String scriptName; // script name equivalent of top, if != top
\r
92 private boolean isSpecLocale; // TRUE if spec is a locale
\r
93 private boolean isNextLocale; // TRUE if nextSpec is a locale
\r
94 private ICUResourceBundle res;
\r
96 public Spec(String theSpec) {
\r
101 // Canonicalize script name. If top is a script name then
\r
102 // script != UScript.INVALID_CODE.
\r
103 int script = UScript.getCodeFromName(top);
\r
105 // Canonicalize script name -or- do locale->script mapping
\r
106 int[] s = UScript.getCode(top);
\r
108 scriptName = UScript.getName(s[0]);
\r
109 // If the script name is the same as top then it's redundant
\r
110 if (scriptName.equalsIgnoreCase(top)) {
\r
115 isSpecLocale = false;
\r
117 // If 'top' is not a script name, try a locale lookup
\r
118 if (script == UScript.INVALID_CODE) {
\r
119 Locale toploc = LocaleUtility.getLocaleFromName(top);
\r
120 res = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_TRANSLIT_BASE_NAME,toploc);
\r
121 // Make sure we got the bundle we wanted; otherwise, don't use it
\r
122 if (res!=null && LocaleUtility.isFallbackOf(res.getULocale().toString(), top)) {
\r
123 isSpecLocale = true;
\r
126 }catch(MissingResourceException e){
\r
129 // assert(spec != top);
\r
133 public boolean hasFallback() {
\r
134 return nextSpec != null;
\r
137 public void reset() {
\r
138 if (spec != top) { // [sic] pointer comparison
\r
140 isSpecLocale = (res != null);
\r
145 private void setupNext() {
\r
146 isNextLocale = false;
\r
147 if (isSpecLocale) {
\r
149 int i = nextSpec.lastIndexOf(LOCALE_SEP);
\r
150 // If i == 0 then we have _FOO, so we fall through
\r
151 // to the scriptName.
\r
153 nextSpec = spec.substring(0, i);
\r
154 isNextLocale = true;
\r
156 nextSpec = scriptName; // scriptName may be null
\r
159 // Fallback to the script, which may be null
\r
160 if (nextSpec != scriptName) {
\r
161 nextSpec = scriptName;
\r
169 // for(String& s(spec.get());
\r
170 // spec.hasFallback(); s(spec.next())) { ...
\r
172 public String next() {
\r
174 isSpecLocale = isNextLocale;
\r
179 public String get() {
\r
183 public boolean isLocale() {
\r
184 return isSpecLocale;
\r
188 * Return the ResourceBundle for this spec, at the current
\r
189 * level of iteration. The level of iteration goes from
\r
190 * aa_BB_CCC to aa_BB to aa. If the bundle does not
\r
191 * correspond to the current level of iteration, return null.
\r
192 * If isLocale() is false, always return null.
\r
194 public ResourceBundle getBundle() {
\r
196 res.getULocale().toString().equals(spec)) {
\r
202 public String getTop() {
\r
207 //----------------------------------------------------------------------
\r
209 //----------------------------------------------------------------------
\r
211 static class ResourceEntry {
\r
212 public String resource;
\r
213 public String encoding;
\r
214 public int direction;
\r
215 public ResourceEntry(String n, String enc, int d) {
\r
222 // An entry representing a rule in a locale resource bundle
\r
223 static class LocaleEntry {
\r
224 public String rule;
\r
225 public int direction;
\r
226 public LocaleEntry(String r, int d) {
\r
232 static class AliasEntry {
\r
233 public String alias;
\r
234 public AliasEntry(String a) {
\r
239 static class CompoundRBTEntry {
\r
241 private Vector idBlockVector;
\r
242 private Vector dataVector;
\r
243 private UnicodeSet compoundFilter;
\r
245 public CompoundRBTEntry(String theID, Vector theIDBlockVector,
\r
246 Vector theDataVector,
\r
247 UnicodeSet theCompoundFilter) {
\r
249 idBlockVector = theIDBlockVector;
\r
250 dataVector = theDataVector;
\r
251 compoundFilter = theCompoundFilter;
\r
254 public Transliterator getInstance() {
\r
255 Vector transliterators = new Vector();
\r
256 int passNumber = 1;
\r
258 int limit = Math.max(idBlockVector.size(), dataVector.size());
\r
259 for (int i = 0; i < limit; i++) {
\r
260 if (i < idBlockVector.size()) {
\r
261 String idBlock = (String)idBlockVector.get(i);
\r
262 if (idBlock.length() > 0)
\r
263 transliterators.add(Transliterator.getInstance(idBlock));
\r
265 if (i < dataVector.size()) {
\r
266 RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data)dataVector.get(i);
\r
267 transliterators.add(new RuleBasedTransliterator("%Pass" + passNumber++, data, null));
\r
271 Transliterator t = new CompoundTransliterator(transliterators, passNumber - 1);
\r
273 if (compoundFilter != null) {
\r
274 t.setFilter(compoundFilter);
\r
280 //----------------------------------------------------------------------
\r
281 // class TransliteratorRegistry: Basic public API
\r
282 //----------------------------------------------------------------------
\r
284 public TransliteratorRegistry() {
\r
285 registry = new Hashtable();
\r
286 specDAG = new Hashtable();
\r
287 availableIDs = new Vector();
\r
291 * Given a simple ID (forward direction, no inline filter, not
\r
292 * compound) attempt to instantiate it from the registry. Return
\r
295 * Return a non-empty aliasReturn value if the ID points to an alias.
\r
296 * We cannot instantiate it ourselves because the alias may contain
\r
297 * filters or compounds, which we do not understand. Caller should
\r
298 * make aliasReturn empty before calling.
\r
300 public Transliterator get(String ID,
\r
301 StringBuffer aliasReturn) {
\r
302 Object[] entry = find(ID);
\r
303 return (entry == null) ? null
\r
304 : instantiateEntry(ID, entry, aliasReturn);
\r
308 * Register a class. This adds an entry to the
\r
309 * dynamic store, or replaces an existing entry. Any entry in the
\r
310 * underlying static locale resource store is masked.
\r
312 public void put(String ID,
\r
313 Class transliteratorSubclass,
\r
315 registerEntry(ID, transliteratorSubclass, visible);
\r
319 * Register an ID and a factory function pointer. This adds an
\r
320 * entry to the dynamic store, or replaces an existing entry. Any
\r
321 * entry in the underlying static locale resource store is masked.
\r
323 public void put(String ID,
\r
324 Transliterator.Factory factory,
\r
326 registerEntry(ID, factory, visible);
\r
330 * Register an ID and a resource name. This adds an entry to the
\r
331 * dynamic store, or replaces an existing entry. Any entry in the
\r
332 * underlying static locale resource store is masked.
\r
334 public void put(String ID,
\r
335 String resourceName,
\r
339 registerEntry(ID, new ResourceEntry(resourceName, encoding, dir), visible);
\r
343 * Register an ID and an alias ID. This adds an entry to the
\r
344 * dynamic store, or replaces an existing entry. Any entry in the
\r
345 * underlying static locale resource store is masked.
\r
347 public void put(String ID,
\r
350 registerEntry(ID, new AliasEntry(alias), visible);
\r
354 * Register an ID and a Transliterator object. This adds an entry
\r
355 * to the dynamic store, or replaces an existing entry. Any entry
\r
356 * in the underlying static locale resource store is masked.
\r
358 public void put(String ID,
\r
359 Transliterator trans,
\r
361 registerEntry(ID, trans, visible);
\r
365 * Unregister an ID. This removes an entry from the dynamic store
\r
366 * if there is one. The static locale resource store is
\r
369 public void remove(String ID) {
\r
370 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
371 // Only need to do this if ID.indexOf('-') < 0
\r
372 String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
\r
373 registry.remove(new CaseInsensitiveString(id));
\r
374 removeSTV(stv[0], stv[1], stv[2]);
\r
375 availableIDs.removeElement(new CaseInsensitiveString(id));
\r
378 //----------------------------------------------------------------------
\r
379 // class TransliteratorRegistry: Public ID and spec management
\r
380 //----------------------------------------------------------------------
\r
383 * An internal class that adapts an enumeration over
\r
384 * CaseInsensitiveStrings to an enumeration over Strings.
\r
386 private static class IDEnumeration implements Enumeration {
\r
389 public IDEnumeration(Enumeration e) {
\r
393 public boolean hasMoreElements() {
\r
394 return en != null && en.hasMoreElements();
\r
397 public Object nextElement() {
\r
398 return ((CaseInsensitiveString) en.nextElement()).getString();
\r
403 * Returns an enumeration over the programmatic names of visible
\r
404 * registered transliterators.
\r
406 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
408 public Enumeration getAvailableIDs() {
\r
409 // Since the cache contains CaseInsensitiveString objects, but
\r
410 // the caller expects Strings, we have to use an intermediary.
\r
411 return new IDEnumeration(availableIDs.elements());
\r
415 * Returns an enumeration over all visible source names.
\r
417 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
419 public Enumeration getAvailableSources() {
\r
420 return new IDEnumeration(specDAG.keys());
\r
424 * Returns an enumeration over visible target names for the given
\r
427 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
429 public Enumeration getAvailableTargets(String source) {
\r
430 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
431 Hashtable targets = (Hashtable) specDAG.get(cisrc);
\r
432 if (targets == null) {
\r
433 return new IDEnumeration(null);
\r
435 return new IDEnumeration(targets.keys());
\r
439 * Returns an enumeration over visible variant names for the given
\r
440 * source and target.
\r
442 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
444 public Enumeration getAvailableVariants(String source, String target) {
\r
445 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
446 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
447 Hashtable targets = (Hashtable) specDAG.get(cisrc);
\r
448 if (targets == null) {
\r
449 return new IDEnumeration(null);
\r
451 Vector variants = (Vector) targets.get(citrg);
\r
452 if (variants == null) {
\r
453 return new IDEnumeration(null);
\r
455 return new IDEnumeration(variants.elements());
\r
458 //----------------------------------------------------------------------
\r
459 // class TransliteratorRegistry: internal
\r
460 //----------------------------------------------------------------------
\r
463 * Convenience method. Calls 6-arg registerEntry().
\r
465 private void registerEntry(String source,
\r
471 if (s.length() == 0) {
\r
474 String ID = TransliteratorIDParser.STVtoID(source, target, variant);
\r
475 registerEntry(ID, s, target, variant, entry, visible);
\r
479 * Convenience method. Calls 6-arg registerEntry().
\r
481 private void registerEntry(String ID,
\r
484 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
485 // Only need to do this if ID.indexOf('-') < 0
\r
486 String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
\r
487 registerEntry(id, stv[0], stv[1], stv[2], entry, visible);
\r
491 * Register an entry object (adopted) with the given ID, source,
\r
492 * target, and variant strings.
\r
494 private void registerEntry(String ID,
\r
500 CaseInsensitiveString ciID = new CaseInsensitiveString(ID);
\r
502 // Store the entry within an array so it can be modified later
\r
503 if (!(entry instanceof Object[])) {
\r
504 entry = new Object[] { entry };
\r
507 registry.put(ciID, entry);
\r
509 registerSTV(source, target, variant);
\r
510 if (!availableIDs.contains(ciID)) {
\r
511 availableIDs.addElement(ciID);
\r
514 removeSTV(source, target, variant);
\r
515 availableIDs.removeElement(ciID);
\r
520 * Register a source-target/variant in the specDAG. Variant may be
\r
521 * empty, but source and target must not be. If variant is empty then
\r
522 * the special variant NO_VARIANT is stored in slot zero of the
\r
523 * UVector of variants.
\r
525 private void registerSTV(String source,
\r
528 // assert(source.length() > 0);
\r
529 // assert(target.length() > 0);
\r
530 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
531 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
532 CaseInsensitiveString civar = new CaseInsensitiveString(variant);
\r
533 Hashtable targets = (Hashtable) specDAG.get(cisrc);
\r
534 if (targets == null) {
\r
535 targets = new Hashtable();
\r
536 specDAG.put(cisrc, targets);
\r
538 Vector variants = (Vector) targets.get(citrg);
\r
539 if (variants == null) {
\r
540 variants = new Vector();
\r
541 targets.put(citrg, variants);
\r
543 // assert(NO_VARIANT == "");
\r
544 // We add the variant string. If it is the special "no variant"
\r
545 // string, that is, the empty string, we add it at position zero.
\r
546 if (!variants.contains(civar)) {
\r
547 if (variant.length() > 0) {
\r
548 variants.addElement(civar);
\r
550 variants.insertElementAt(civar, 0);
\r
556 * Remove a source-target/variant from the specDAG.
\r
558 private void removeSTV(String source,
\r
561 // assert(source.length() > 0);
\r
562 // assert(target.length() > 0);
\r
563 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
564 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
565 CaseInsensitiveString civar = new CaseInsensitiveString(variant);
\r
566 Hashtable targets = (Hashtable) specDAG.get(cisrc);
\r
567 if (targets == null) {
\r
568 return; // should never happen for valid s-t/v
\r
570 Vector variants = (Vector) targets.get(citrg);
\r
571 if (variants == null) {
\r
572 return; // should never happen for valid s-t/v
\r
574 variants.removeElement(civar);
\r
575 if (variants.size() == 0) {
\r
576 targets.remove(citrg); // should delete variants
\r
577 if (targets.size() == 0) {
\r
578 specDAG.remove(cisrc); // should delete targets
\r
583 private static final boolean DEBUG = false;
\r
586 * Attempt to find a source-target/variant in the dynamic registry
\r
587 * store. Return 0 on failure.
\r
589 private Object[] findInDynamicStore(Spec src,
\r
592 String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
\r
594 System.out.println("TransliteratorRegistry.findInDynamicStore:" +
\r
597 return (Object[]) registry.get(new CaseInsensitiveString(ID));
\r
601 * Attempt to find a source-target/variant in the static locale
\r
602 * resource store. Do not perform fallback. Return 0 on failure.
\r
604 * On success, create a new entry object, register it in the dynamic
\r
605 * store, and return a pointer to it, but do not make it public --
\r
606 * just because someone requested something, we do not expand the
\r
607 * available ID list (or spec DAG).
\r
609 private Object[] findInStaticStore(Spec src,
\r
613 String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
\r
614 System.out.println("TransliteratorRegistry.findInStaticStore:" +
\r
617 Object[] entry = null;
\r
618 if (src.isLocale()) {
\r
619 entry = findInBundle(src, trg, variant, Transliterator.FORWARD);
\r
620 } else if (trg.isLocale()) {
\r
621 entry = findInBundle(trg, src, variant, Transliterator.REVERSE);
\r
624 // If we found an entry, store it in the Hashtable for next
\r
626 if (entry != null) {
\r
627 registerEntry(src.getTop(), trg.getTop(), variant, entry, false);
\r
634 * Attempt to find an entry in a single resource bundle. This is
\r
635 * a one-sided lookup. findInStaticStore() performs up to two such
\r
636 * lookups, one for the source, and one for the target.
\r
638 * Do not perform fallback. Return 0 on failure.
\r
640 * On success, create a new Entry object, populate it, and return it.
\r
641 * The caller owns the returned object.
\r
643 private Object[] findInBundle(Spec specToOpen,
\r
647 // assert(specToOpen.isLocale());
\r
648 ResourceBundle res = specToOpen.getBundle();
\r
651 // This means that the bundle's locale does not match
\r
652 // the current level of iteration for the spec.
\r
656 for (int pass=0; pass<2; ++pass) {
\r
657 StringBuffer tag = new StringBuffer();
\r
658 // First try either TransliteratorTo_xxx or
\r
659 // TransliterateFrom_xxx, then try the bidirectional
\r
660 // Transliterate_xxx. This precedence order is arbitrary
\r
661 // but must be consistent and documented.
\r
663 tag.append(direction == Transliterator.FORWARD ?
\r
664 "TransliterateTo" : "TransliterateFrom");
\r
666 tag.append("Transliterate");
\r
668 tag.append(specToFind.get().toUpperCase());
\r
671 // The Transliterate*_xxx resource is an array of
\r
672 // strings of the format { <v0>, <r0>, ... }. Each
\r
673 // <vi> is a variant name, and each <ri> is a rule.
\r
674 String[] subres = res.getStringArray(tag.toString());
\r
676 // assert(subres != null);
\r
677 // assert(subres.length % 2 == 0);
\r
679 if (variant.length() != 0) {
\r
680 for (i=0; i<subres.length; i+= 2) {
\r
681 if (subres[i].equalsIgnoreCase(variant)) {
\r
687 if (i < subres.length) {
\r
688 // We have a match, or there is no variant and i == 0.
\r
689 // We have succeeded in loading a string from the
\r
690 // locale resources. Return the rule string which
\r
691 // will itself become the registry entry.
\r
693 // The direction is always forward for the
\r
694 // TransliterateTo_xxx and TransliterateFrom_xxx
\r
695 // items; those are unidirectional forward rules.
\r
696 // For the bidirectional Transliterate_xxx items,
\r
697 // the direction is the value passed in to this
\r
699 int dir = (pass == 0) ? Transliterator.FORWARD : direction;
\r
700 return new Object[] { new LocaleEntry(subres[i+1], dir) };
\r
703 } catch (MissingResourceException e) {
\r
704 if (DEBUG) System.out.println("missing resource: " + e);
\r
708 // If we get here we had a missing resource exception or we
\r
709 // failed to find a desired variant.
\r
714 * Convenience method. Calls 3-arg find().
\r
716 private Object[] find(String ID) {
\r
717 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
718 return find(stv[0], stv[1], stv[2]);
\r
722 * Top-level find method. Attempt to find a source-target/variant in
\r
723 * either the dynamic or the static (locale resource) store. Perform
\r
726 * Lookup sequence for ss_SS_SSS-tt_TT_TTT/v:
\r
728 * ss_SS_SSS-tt_TT_TTT/v -- in hashtable
\r
729 * ss_SS_SSS-tt_TT_TTT/v -- in ss_SS_SSS (no fallback)
\r
731 * repeat with t = tt_TT_TTT, tt_TT, tt, and tscript
\r
738 * Here * matches the first variant listed.
\r
740 * Caller does NOT own returned object. Return 0 on failure.
\r
742 private Object[] find(String source,
\r
746 Spec src = new Spec(source);
\r
747 Spec trg = new Spec(target);
\r
748 Object[] entry = null;
\r
750 if (variant.length() != 0) {
\r
752 // Seek exact match in hashtable
\r
753 entry = findInDynamicStore(src, trg, variant);
\r
754 if (entry != null) {
\r
758 // Seek exact match in locale resources
\r
759 entry = findInStaticStore(src, trg, variant);
\r
760 if (entry != null) {
\r
768 // Seek match in hashtable
\r
769 entry = findInDynamicStore(src, trg, NO_VARIANT);
\r
770 if (entry != null) {
\r
774 // Seek match in locale resources
\r
775 entry = findInStaticStore(src, trg, NO_VARIANT);
\r
776 if (entry != null) {
\r
779 if (!src.hasFallback()) {
\r
784 if (!trg.hasFallback()) {
\r
794 * Given an Entry object, instantiate it. Caller owns result. Return
\r
797 * Return a non-empty aliasReturn value if the ID points to an alias.
\r
798 * We cannot instantiate it ourselves because the alias may contain
\r
799 * filters or compounds, which we do not understand. Caller should
\r
800 * make aliasReturn empty before calling.
\r
802 * The entry object is assumed to reside in the dynamic store. It may be
\r
805 private Transliterator instantiateEntry(String ID,
\r
806 Object[] entryWrapper,
\r
807 StringBuffer aliasReturn) {
\r
808 // We actually modify the entry object in some cases. If it
\r
809 // is a string, we may partially parse it and turn it into a
\r
810 // more processed precursor. This makes the next
\r
811 // instantiation faster and allows sharing of immutable
\r
812 // components like the RuleBasedTransliterator.Data objects.
\r
813 // For this reason, the entry object is an Object[] of length
\r
817 Object entry = entryWrapper[0];
\r
819 if (entry instanceof RuleBasedTransliterator.Data) {
\r
820 RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) entry;
\r
821 return new RuleBasedTransliterator(ID, data, null);
\r
822 } else if (entry instanceof Class) {
\r
824 return (Transliterator) ((Class) entry).newInstance();
\r
825 } catch (InstantiationException e) {
\r
826 } catch (IllegalAccessException e2) {}
\r
828 } else if (entry instanceof AliasEntry) {
\r
829 aliasReturn.append(((AliasEntry) entry).alias);
\r
831 } else if (entry instanceof Transliterator.Factory) {
\r
832 return ((Transliterator.Factory) entry).getInstance(ID);
\r
833 } else if (entry instanceof CompoundRBTEntry) {
\r
834 return ((CompoundRBTEntry) entry).getInstance();
\r
835 } else if (entry instanceof AnyTransliterator) {
\r
836 AnyTransliterator temp = (AnyTransliterator) entry;
\r
837 return temp.safeClone();
\r
838 } else if (entry instanceof RuleBasedTransliterator) {
\r
839 RuleBasedTransliterator temp = (RuleBasedTransliterator) entry;
\r
840 return temp.safeClone();
\r
841 } else if (entry instanceof CompoundTransliterator) {
\r
842 CompoundTransliterator temp = (CompoundTransliterator) entry;
\r
843 return temp.safeClone();
\r
844 } else if (entry instanceof Transliterator) {
\r
845 return (Transliterator) entry;
\r
848 // At this point entry type must be either RULES_FORWARD or
\r
849 // RULES_REVERSE. We process the rule data into a
\r
850 // TransliteratorRuleData object, and possibly also into an
\r
851 // .id header and/or footer. Then we modify the registry with
\r
852 // the parsed data and retry.
\r
854 TransliteratorParser parser = new TransliteratorParser();
\r
858 ResourceEntry re = (ResourceEntry) entry;
\r
859 parser.parse(re.resource, re.direction);
\r
861 } catch (ClassCastException e) {
\r
862 // If we pull a rule from a locale resource bundle it will
\r
863 // be a LocaleEntry.
\r
864 LocaleEntry le = (LocaleEntry) entry;
\r
865 parser.parse(le.rule, le.direction);
\r
868 // Reset entry to something that we process at the
\r
869 // top of the loop, then loop back to the top. As long as we
\r
870 // do this, we only loop through twice at most.
\r
871 // NOTE: The logic here matches that in
\r
872 // Transliterator.createFromRules().
\r
873 if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) {
\r
874 // No idBlock, no data -- this is just an
\r
876 entryWrapper[0] = new AliasEntry(NullTransliterator._ID);
\r
878 else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) {
\r
879 // No idBlock, data != 0 -- this is an
\r
880 // ordinary RBT_DATA
\r
881 entryWrapper[0] = parser.dataVector.get(0);
\r
883 else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) {
\r
884 // idBlock, no data -- this is an alias. The ID has
\r
885 // been munged from reverse into forward mode, if
\r
886 // necessary, so instantiate the ID in the forward
\r
888 if (parser.compoundFilter != null)
\r
889 entryWrapper[0] = new AliasEntry(parser.compoundFilter.toPattern(false) + ";"
\r
890 + (String)parser.idBlockVector.get(0));
\r
892 entryWrapper[0] = new AliasEntry((String)parser.idBlockVector.get(0));
\r
895 entryWrapper[0] = new CompoundRBTEntry(ID, parser.idBlockVector, parser.dataVector,
\r
896 parser.compoundFilter);
\r