2 **********************************************************************
\r
3 * Copyright (c) 2001-2010, 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 java.util.Enumeration;
\r
14 import java.util.Hashtable;
\r
15 import java.util.Locale;
\r
16 import java.util.MissingResourceException;
\r
17 import java.util.ResourceBundle;
\r
18 import java.util.Vector;
\r
20 import com.ibm.icu.impl.ICUResourceBundle;
\r
21 import com.ibm.icu.impl.LocaleUtility;
\r
22 import com.ibm.icu.lang.UScript;
\r
23 import com.ibm.icu.text.RuleBasedTransliterator.Data;
\r
24 import com.ibm.icu.util.CaseInsensitiveString;
\r
25 import com.ibm.icu.util.UResourceBundle;
\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<CaseInsensitiveString, Object[]> 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<CaseInsensitiveString, Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>>> specDAG;
\r
63 * Vector of public full IDs (CaseInsensitiveString objects).
\r
65 private Vector<CaseInsensitiveString> 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
128 // The constructor is called from multiple private methods
\r
129 // that protects an invalid scriptName
\r
133 // assert(spec != top);
\r
137 public boolean hasFallback() {
\r
138 return nextSpec != null;
\r
141 public void reset() {
\r
142 if (spec != top) { // [sic] pointer comparison
\r
144 isSpecLocale = (res != null);
\r
149 private void setupNext() {
\r
150 isNextLocale = false;
\r
151 if (isSpecLocale) {
\r
153 int i = nextSpec.lastIndexOf(LOCALE_SEP);
\r
154 // If i == 0 then we have _FOO, so we fall through
\r
155 // to the scriptName.
\r
157 nextSpec = spec.substring(0, i);
\r
158 isNextLocale = true;
\r
160 nextSpec = scriptName; // scriptName may be null
\r
163 // Fallback to the script, which may be null
\r
164 if (nextSpec != scriptName) {
\r
165 nextSpec = scriptName;
\r
173 // for(String& s(spec.get());
\r
174 // spec.hasFallback(); s(spec.next())) { ...
\r
176 public String next() {
\r
178 isSpecLocale = isNextLocale;
\r
183 public String get() {
\r
187 public boolean isLocale() {
\r
188 return isSpecLocale;
\r
192 * Return the ResourceBundle for this spec, at the current
\r
193 * level of iteration. The level of iteration goes from
\r
194 * aa_BB_CCC to aa_BB to aa. If the bundle does not
\r
195 * correspond to the current level of iteration, return null.
\r
196 * If isLocale() is false, always return null.
\r
198 public ResourceBundle getBundle() {
\r
200 res.getULocale().toString().equals(spec)) {
\r
206 public String getTop() {
\r
211 //----------------------------------------------------------------------
\r
213 //----------------------------------------------------------------------
\r
215 static class ResourceEntry {
\r
216 public String resource;
\r
217 public String encoding;
\r
218 public int direction;
\r
219 public ResourceEntry(String n, String enc, int d) {
\r
226 // An entry representing a rule in a locale resource bundle
\r
227 static class LocaleEntry {
\r
228 public String rule;
\r
229 public int direction;
\r
230 public LocaleEntry(String r, int d) {
\r
236 static class AliasEntry {
\r
237 public String alias;
\r
238 public AliasEntry(String a) {
\r
243 static class CompoundRBTEntry {
\r
245 private Vector<String> idBlockVector;
\r
246 private Vector<Data> dataVector;
\r
247 private UnicodeSet compoundFilter;
\r
249 public CompoundRBTEntry(String theID, Vector<String> theIDBlockVector,
\r
250 Vector<Data> theDataVector,
\r
251 UnicodeSet theCompoundFilter) {
\r
253 idBlockVector = theIDBlockVector;
\r
254 dataVector = theDataVector;
\r
255 compoundFilter = theCompoundFilter;
\r
258 public Transliterator getInstance() {
\r
259 Vector<Transliterator> transliterators = new Vector<Transliterator>();
\r
260 int passNumber = 1;
\r
262 int limit = Math.max(idBlockVector.size(), dataVector.size());
\r
263 for (int i = 0; i < limit; i++) {
\r
264 if (i < idBlockVector.size()) {
\r
265 String idBlock = idBlockVector.get(i);
\r
266 if (idBlock.length() > 0)
\r
267 transliterators.add(Transliterator.getInstance(idBlock));
\r
269 if (i < dataVector.size()) {
\r
270 Data data = dataVector.get(i);
\r
271 transliterators.add(new RuleBasedTransliterator("%Pass" + passNumber++, data, null));
\r
275 Transliterator t = new CompoundTransliterator(transliterators, passNumber - 1);
\r
277 if (compoundFilter != null) {
\r
278 t.setFilter(compoundFilter);
\r
284 //----------------------------------------------------------------------
\r
285 // class TransliteratorRegistry: Basic public API
\r
286 //----------------------------------------------------------------------
\r
288 public TransliteratorRegistry() {
\r
289 registry = new Hashtable<CaseInsensitiveString, Object[]>();
\r
290 specDAG = new Hashtable<CaseInsensitiveString, Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>>>();
\r
291 availableIDs = new Vector<CaseInsensitiveString>();
\r
295 * Given a simple ID (forward direction, no inline filter, not
\r
296 * compound) attempt to instantiate it from the registry. Return
\r
299 * Return a non-empty aliasReturn value if the ID points to an alias.
\r
300 * We cannot instantiate it ourselves because the alias may contain
\r
301 * filters or compounds, which we do not understand. Caller should
\r
302 * make aliasReturn empty before calling.
\r
304 public Transliterator get(String ID,
\r
305 StringBuffer aliasReturn) {
\r
306 Object[] entry = find(ID);
\r
307 return (entry == null) ? null
\r
308 : instantiateEntry(ID, entry, aliasReturn);
\r
312 * Register a class. This adds an entry to the
\r
313 * dynamic store, or replaces an existing entry. Any entry in the
\r
314 * underlying static locale resource store is masked.
\r
316 public void put(String ID,
\r
317 Class<? extends Transliterator> transliteratorSubclass,
\r
319 registerEntry(ID, transliteratorSubclass, visible);
\r
323 * Register an ID and a factory function pointer. This adds an
\r
324 * entry to the dynamic store, or replaces an existing entry. Any
\r
325 * entry in the underlying static locale resource store is masked.
\r
327 public void put(String ID,
\r
328 Transliterator.Factory factory,
\r
330 registerEntry(ID, factory, visible);
\r
334 * Register an ID and a resource name. This adds an entry to the
\r
335 * dynamic store, or replaces an existing entry. Any entry in the
\r
336 * underlying static locale resource store is masked.
\r
338 public void put(String ID,
\r
339 String resourceName,
\r
343 registerEntry(ID, new ResourceEntry(resourceName, encoding, dir), visible);
\r
347 * Register an ID and an alias ID. This adds an entry to the
\r
348 * dynamic store, or replaces an existing entry. Any entry in the
\r
349 * underlying static locale resource store is masked.
\r
351 public void put(String ID,
\r
354 registerEntry(ID, new AliasEntry(alias), visible);
\r
358 * Register an ID and a Transliterator object. This adds an entry
\r
359 * to the dynamic store, or replaces an existing entry. Any entry
\r
360 * in the underlying static locale resource store is masked.
\r
362 public void put(String ID,
\r
363 Transliterator trans,
\r
365 registerEntry(ID, trans, visible);
\r
369 * Unregister an ID. This removes an entry from the dynamic store
\r
370 * if there is one. The static locale resource store is
\r
373 public void remove(String ID) {
\r
374 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
375 // Only need to do this if ID.indexOf('-') < 0
\r
376 String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
\r
377 registry.remove(new CaseInsensitiveString(id));
\r
378 removeSTV(stv[0], stv[1], stv[2]);
\r
379 availableIDs.removeElement(new CaseInsensitiveString(id));
\r
382 //----------------------------------------------------------------------
\r
383 // class TransliteratorRegistry: Public ID and spec management
\r
384 //----------------------------------------------------------------------
\r
387 * An internal class that adapts an enumeration over
\r
388 * CaseInsensitiveStrings to an enumeration over Strings.
\r
390 private static class IDEnumeration implements Enumeration<String> {
\r
391 Enumeration<CaseInsensitiveString> en;
\r
393 public IDEnumeration(Enumeration<CaseInsensitiveString> e) {
\r
397 public boolean hasMoreElements() {
\r
398 return en != null && en.hasMoreElements();
\r
401 public String nextElement() {
\r
402 return (en.nextElement()).getString();
\r
407 * Returns an enumeration over the programmatic names of visible
\r
408 * registered transliterators.
\r
410 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
412 public Enumeration<String> getAvailableIDs() {
\r
413 // Since the cache contains CaseInsensitiveString objects, but
\r
414 // the caller expects Strings, we have to use an intermediary.
\r
415 return new IDEnumeration(availableIDs.elements());
\r
419 * Returns an enumeration over all visible source names.
\r
421 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
423 public Enumeration<String> getAvailableSources() {
\r
424 return new IDEnumeration(specDAG.keys());
\r
428 * Returns an enumeration over visible target names for the given
\r
431 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
433 public Enumeration<String> getAvailableTargets(String source) {
\r
434 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
435 Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>> targets = specDAG.get(cisrc);
\r
436 if (targets == null) {
\r
437 return new IDEnumeration(null);
\r
439 return new IDEnumeration(targets.keys());
\r
443 * Returns an enumeration over visible variant names for the given
\r
444 * source and target.
\r
446 * @return An <code>Enumeration</code> over <code>String</code> objects
\r
448 public Enumeration<String> getAvailableVariants(String source, String target) {
\r
449 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
450 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
451 Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>> targets = specDAG.get(cisrc);
\r
452 if (targets == null) {
\r
453 return new IDEnumeration(null);
\r
455 Vector<CaseInsensitiveString> variants = targets.get(citrg);
\r
456 if (variants == null) {
\r
457 return new IDEnumeration(null);
\r
459 return new IDEnumeration(variants.elements());
\r
462 //----------------------------------------------------------------------
\r
463 // class TransliteratorRegistry: internal
\r
464 //----------------------------------------------------------------------
\r
467 * Convenience method. Calls 6-arg registerEntry().
\r
469 private void registerEntry(String source,
\r
475 if (s.length() == 0) {
\r
478 String ID = TransliteratorIDParser.STVtoID(source, target, variant);
\r
479 registerEntry(ID, s, target, variant, entry, visible);
\r
483 * Convenience method. Calls 6-arg registerEntry().
\r
485 private void registerEntry(String ID,
\r
488 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
489 // Only need to do this if ID.indexOf('-') < 0
\r
490 String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
\r
491 registerEntry(id, stv[0], stv[1], stv[2], entry, visible);
\r
495 * Register an entry object (adopted) with the given ID, source,
\r
496 * target, and variant strings.
\r
498 private void registerEntry(String ID,
\r
504 CaseInsensitiveString ciID = new CaseInsensitiveString(ID);
\r
505 Object[] arrayOfObj;
\r
507 // Store the entry within an array so it can be modified later
\r
508 if (entry instanceof Object[]) {
\r
509 arrayOfObj = (Object[])entry;
\r
511 arrayOfObj = new Object[] { entry };
\r
514 registry.put(ciID, arrayOfObj);
\r
516 registerSTV(source, target, variant);
\r
517 if (!availableIDs.contains(ciID)) {
\r
518 availableIDs.addElement(ciID);
\r
521 removeSTV(source, target, variant);
\r
522 availableIDs.removeElement(ciID);
\r
527 * Register a source-target/variant in the specDAG. Variant may be
\r
528 * empty, but source and target must not be. If variant is empty then
\r
529 * the special variant NO_VARIANT is stored in slot zero of the
\r
530 * UVector of variants.
\r
532 private void registerSTV(String source,
\r
535 // assert(source.length() > 0);
\r
536 // assert(target.length() > 0);
\r
537 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
538 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
539 CaseInsensitiveString civar = new CaseInsensitiveString(variant);
\r
540 Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>> targets = specDAG.get(cisrc);
\r
541 if (targets == null) {
\r
542 targets = new Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>>();
\r
543 specDAG.put(cisrc, targets);
\r
545 Vector<CaseInsensitiveString> variants = targets.get(citrg);
\r
546 if (variants == null) {
\r
547 variants = new Vector<CaseInsensitiveString>();
\r
548 targets.put(citrg, variants);
\r
550 // assert(NO_VARIANT == "");
\r
551 // We add the variant string. If it is the special "no variant"
\r
552 // string, that is, the empty string, we add it at position zero.
\r
553 if (!variants.contains(civar)) {
\r
554 if (variant.length() > 0) {
\r
555 variants.addElement(civar);
\r
557 variants.insertElementAt(civar, 0);
\r
563 * Remove a source-target/variant from the specDAG.
\r
565 private void removeSTV(String source,
\r
568 // assert(source.length() > 0);
\r
569 // assert(target.length() > 0);
\r
570 CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
\r
571 CaseInsensitiveString citrg = new CaseInsensitiveString(target);
\r
572 CaseInsensitiveString civar = new CaseInsensitiveString(variant);
\r
573 Hashtable<CaseInsensitiveString, Vector<CaseInsensitiveString>> targets = specDAG.get(cisrc);
\r
574 if (targets == null) {
\r
575 return; // should never happen for valid s-t/v
\r
577 Vector<CaseInsensitiveString> variants = targets.get(citrg);
\r
578 if (variants == null) {
\r
579 return; // should never happen for valid s-t/v
\r
581 variants.removeElement(civar);
\r
582 if (variants.size() == 0) {
\r
583 targets.remove(citrg); // should delete variants
\r
584 if (targets.size() == 0) {
\r
585 specDAG.remove(cisrc); // should delete targets
\r
590 private static final boolean DEBUG = false;
\r
593 * Attempt to find a source-target/variant in the dynamic registry
\r
594 * store. Return 0 on failure.
\r
596 private Object[] findInDynamicStore(Spec src,
\r
599 String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
\r
602 System.out.println("TransliteratorRegistry.findInDynamicStore:" +
\r
606 return registry.get(new CaseInsensitiveString(ID));
\r
610 * Attempt to find a source-target/variant in the static locale
\r
611 * resource store. Do not perform fallback. Return 0 on failure.
\r
613 * On success, create a new entry object, register it in the dynamic
\r
614 * store, and return a pointer to it, but do not make it public --
\r
615 * just because someone requested something, we do not expand the
\r
616 * available ID list (or spec DAG).
\r
618 private Object[] findInStaticStore(Spec src,
\r
623 String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
\r
624 System.out.println("TransliteratorRegistry.findInStaticStore:" +
\r
628 Object[] entry = null;
\r
629 if (src.isLocale()) {
\r
630 entry = findInBundle(src, trg, variant, Transliterator.FORWARD);
\r
631 } else if (trg.isLocale()) {
\r
632 entry = findInBundle(trg, src, variant, Transliterator.REVERSE);
\r
635 // If we found an entry, store it in the Hashtable for next
\r
637 if (entry != null) {
\r
638 registerEntry(src.getTop(), trg.getTop(), variant, entry, false);
\r
645 * Attempt to find an entry in a single resource bundle. This is
\r
646 * a one-sided lookup. findInStaticStore() performs up to two such
\r
647 * lookups, one for the source, and one for the target.
\r
649 * Do not perform fallback. Return 0 on failure.
\r
651 * On success, create a new Entry object, populate it, and return it.
\r
652 * The caller owns the returned object.
\r
654 private Object[] findInBundle(Spec specToOpen,
\r
658 // assert(specToOpen.isLocale());
\r
659 ResourceBundle res = specToOpen.getBundle();
\r
662 // This means that the bundle's locale does not match
\r
663 // the current level of iteration for the spec.
\r
667 for (int pass=0; pass<2; ++pass) {
\r
668 StringBuilder tag = new StringBuilder();
\r
669 // First try either TransliteratorTo_xxx or
\r
670 // TransliterateFrom_xxx, then try the bidirectional
\r
671 // Transliterate_xxx. This precedence order is arbitrary
\r
672 // but must be consistent and documented.
\r
674 tag.append(direction == Transliterator.FORWARD ?
\r
675 "TransliterateTo" : "TransliterateFrom");
\r
677 tag.append("Transliterate");
\r
679 tag.append(specToFind.get().toUpperCase());
\r
682 // The Transliterate*_xxx resource is an array of
\r
683 // strings of the format { <v0>, <r0>, ... }. Each
\r
684 // <vi> is a variant name, and each <ri> is a rule.
\r
685 String[] subres = res.getStringArray(tag.toString());
\r
687 // assert(subres != null);
\r
688 // assert(subres.length % 2 == 0);
\r
690 if (variant.length() != 0) {
\r
691 for (i=0; i<subres.length; i+= 2) {
\r
692 if (subres[i].equalsIgnoreCase(variant)) {
\r
698 if (i < subres.length) {
\r
699 // We have a match, or there is no variant and i == 0.
\r
700 // We have succeeded in loading a string from the
\r
701 // locale resources. Return the rule string which
\r
702 // will itself become the registry entry.
\r
704 // The direction is always forward for the
\r
705 // TransliterateTo_xxx and TransliterateFrom_xxx
\r
706 // items; those are unidirectional forward rules.
\r
707 // For the bidirectional Transliterate_xxx items,
\r
708 // the direction is the value passed in to this
\r
710 int dir = (pass == 0) ? Transliterator.FORWARD : direction;
\r
711 return new Object[] { new LocaleEntry(subres[i+1], dir) };
\r
714 } catch (MissingResourceException e) {
\r
716 if (DEBUG) System.out.println("missing resource: " + e);
\r
721 // If we get here we had a missing resource exception or we
\r
722 // failed to find a desired variant.
\r
727 * Convenience method. Calls 3-arg find().
\r
729 private Object[] find(String ID) {
\r
730 String[] stv = TransliteratorIDParser.IDtoSTV(ID);
\r
731 return find(stv[0], stv[1], stv[2]);
\r
735 * Top-level find method. Attempt to find a source-target/variant in
\r
736 * either the dynamic or the static (locale resource) store. Perform
\r
739 * Lookup sequence for ss_SS_SSS-tt_TT_TTT/v:
\r
741 * ss_SS_SSS-tt_TT_TTT/v -- in hashtable
\r
742 * ss_SS_SSS-tt_TT_TTT/v -- in ss_SS_SSS (no fallback)
\r
744 * repeat with t = tt_TT_TTT, tt_TT, tt, and tscript
\r
751 * Here * matches the first variant listed.
\r
753 * Caller does NOT own returned object. Return 0 on failure.
\r
755 private Object[] find(String source,
\r
759 Spec src = new Spec(source);
\r
760 Spec trg = new Spec(target);
\r
761 Object[] entry = null;
\r
763 if (variant.length() != 0) {
\r
765 // Seek exact match in hashtable
\r
766 entry = findInDynamicStore(src, trg, variant);
\r
767 if (entry != null) {
\r
771 // Seek exact match in locale resources
\r
772 entry = findInStaticStore(src, trg, variant);
\r
773 if (entry != null) {
\r
781 // Seek match in hashtable
\r
782 entry = findInDynamicStore(src, trg, NO_VARIANT);
\r
783 if (entry != null) {
\r
787 // Seek match in locale resources
\r
788 entry = findInStaticStore(src, trg, NO_VARIANT);
\r
789 if (entry != null) {
\r
792 if (!src.hasFallback()) {
\r
797 if (!trg.hasFallback()) {
\r
807 * Given an Entry object, instantiate it. Caller owns result. Return
\r
810 * Return a non-empty aliasReturn value if the ID points to an alias.
\r
811 * We cannot instantiate it ourselves because the alias may contain
\r
812 * filters or compounds, which we do not understand. Caller should
\r
813 * make aliasReturn empty before calling.
\r
815 * The entry object is assumed to reside in the dynamic store. It may be
\r
818 @SuppressWarnings("unchecked")
\r
819 private Transliterator instantiateEntry(String ID,
\r
820 Object[] entryWrapper,
\r
821 StringBuffer aliasReturn) {
\r
822 // We actually modify the entry object in some cases. If it
\r
823 // is a string, we may partially parse it and turn it into a
\r
824 // more processed precursor. This makes the next
\r
825 // instantiation faster and allows sharing of immutable
\r
826 // components like the RuleBasedTransliterator.Data objects.
\r
827 // For this reason, the entry object is an Object[] of length
\r
831 Object entry = entryWrapper[0];
\r
833 if (entry instanceof RuleBasedTransliterator.Data) {
\r
834 RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) entry;
\r
835 return new RuleBasedTransliterator(ID, data, null);
\r
836 } else if (entry instanceof Class) {
\r
838 return (Transliterator) ((Class) entry).newInstance();
\r
839 } catch (InstantiationException e) {
\r
840 } catch (IllegalAccessException e2) {}
\r
842 } else if (entry instanceof AliasEntry) {
\r
843 aliasReturn.append(((AliasEntry) entry).alias);
\r
845 } else if (entry instanceof Transliterator.Factory) {
\r
846 return ((Transliterator.Factory) entry).getInstance(ID);
\r
847 } else if (entry instanceof CompoundRBTEntry) {
\r
848 return ((CompoundRBTEntry) entry).getInstance();
\r
849 } else if (entry instanceof AnyTransliterator) {
\r
850 AnyTransliterator temp = (AnyTransliterator) entry;
\r
851 return temp.safeClone();
\r
852 } else if (entry instanceof RuleBasedTransliterator) {
\r
853 RuleBasedTransliterator temp = (RuleBasedTransliterator) entry;
\r
854 return temp.safeClone();
\r
855 } else if (entry instanceof CompoundTransliterator) {
\r
856 CompoundTransliterator temp = (CompoundTransliterator) entry;
\r
857 return temp.safeClone();
\r
858 } else if (entry instanceof Transliterator) {
\r
859 return (Transliterator) entry;
\r
862 // At this point entry type must be either RULES_FORWARD or
\r
863 // RULES_REVERSE. We process the rule data into a
\r
864 // TransliteratorRuleData object, and possibly also into an
\r
865 // .id header and/or footer. Then we modify the registry with
\r
866 // the parsed data and retry.
\r
868 TransliteratorParser parser = new TransliteratorParser();
\r
872 ResourceEntry re = (ResourceEntry) entry;
\r
873 parser.parse(re.resource, re.direction);
\r
875 } catch (ClassCastException e) {
\r
876 // If we pull a rule from a locale resource bundle it will
\r
877 // be a LocaleEntry.
\r
878 LocaleEntry le = (LocaleEntry) entry;
\r
879 parser.parse(le.rule, le.direction);
\r
882 // Reset entry to something that we process at the
\r
883 // top of the loop, then loop back to the top. As long as we
\r
884 // do this, we only loop through twice at most.
\r
885 // NOTE: The logic here matches that in
\r
886 // Transliterator.createFromRules().
\r
887 if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) {
\r
888 // No idBlock, no data -- this is just an
\r
890 entryWrapper[0] = new AliasEntry(NullTransliterator._ID);
\r
892 else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) {
\r
893 // No idBlock, data != 0 -- this is an
\r
894 // ordinary RBT_DATA
\r
895 entryWrapper[0] = parser.dataVector.get(0);
\r
897 else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) {
\r
898 // idBlock, no data -- this is an alias. The ID has
\r
899 // been munged from reverse into forward mode, if
\r
900 // necessary, so instantiate the ID in the forward
\r
902 if (parser.compoundFilter != null) {
\r
903 entryWrapper[0] = new AliasEntry(parser.compoundFilter.toPattern(false) + ";"
\r
904 + parser.idBlockVector.get(0));
\r
906 entryWrapper[0] = new AliasEntry(parser.idBlockVector.get(0));
\r
910 entryWrapper[0] = new CompoundRBTEntry(ID, parser.idBlockVector, parser.dataVector,
\r
911 parser.compoundFilter);
\r