001: /*
002: **********************************************************************
003: * Copyright (c) 2001-2005, International Business Machines
004: * Corporation and others. All Rights Reserved.
005: **********************************************************************
006: * Date Name Description
007: * 08/19/2001 aliu Creation.
008: **********************************************************************
009: */
010:
011: package com.ibm.icu.text;
012:
013: //import com.ibm.icu.impl.ICULocaleData;
014: import com.ibm.icu.impl.ICUResourceBundle;
015: import com.ibm.icu.impl.LocaleUtility;
016: import com.ibm.icu.lang.UScript;
017: import com.ibm.icu.util.CaseInsensitiveString;
018: import com.ibm.icu.util.UResourceBundle;
019:
020: import java.util.Enumeration;
021: import java.util.Hashtable;
022: import java.util.Locale;
023: import java.util.MissingResourceException;
024: import java.util.ResourceBundle;
025: import java.util.Vector;
026:
027: class TransliteratorRegistry {
028:
029: // char constants
030: private static final char LOCALE_SEP = '_';
031:
032: // String constants
033: private static final String NO_VARIANT = ""; // empty string
034: private static final String ANY = "Any";
035:
036: /**
037: * Dynamic registry mapping full IDs to Entry objects. This
038: * contains both public and internal entities. The visibility is
039: * controlled by whether an entry is listed in availableIDs and
040: * specDAG or not.
041: *
042: * Keys are CaseInsensitiveString objects.
043: * Values are objects of class Class (subclass of Transliterator),
044: * RuleBasedTransliterator.Data, Transliterator.Factory, or one
045: * of the entry classes defined here (AliasEntry or ResourceEntry).
046: */
047: private Hashtable registry;
048:
049: /**
050: * DAG of visible IDs by spec. Hashtable: source => (Hashtable:
051: * target => (Vector: variant)) The Vector of variants is never
052: * empty. For a source-target with no variant, the special
053: * variant NO_VARIANT (the empty string) is stored in slot zero of
054: * the UVector.
055: *
056: * Keys are CaseInsensitiveString objects.
057: * Values are Hashtable of (CaseInsensitiveString -> Vector of
058: * CaseInsensitiveString)
059: */
060: private Hashtable specDAG;
061:
062: /**
063: * Vector of public full IDs (CaseInsensitiveString objects).
064: */
065: private Vector availableIDs;
066:
067: //----------------------------------------------------------------------
068: // class Spec
069: //----------------------------------------------------------------------
070:
071: /**
072: * A Spec is a string specifying either a source or a target. In more
073: * general terms, it may also specify a variant, but we only use the
074: * Spec class for sources and targets.
075: *
076: * A Spec may be a locale or a script. If it is a locale, it has a
077: * fallback chain that goes xx_YY_ZZZ -> xx_YY -> xx -> ssss, where
078: * ssss is the script mapping of xx_YY_ZZZ. The Spec API methods
079: * hasFallback(), next(), and reset() iterate over this fallback
080: * sequence.
081: *
082: * The Spec class canonicalizes itself, so the locale is put into
083: * canonical form, or the script is transformed from an abbreviation
084: * to a full name.
085: */
086: static class Spec {
087:
088: private String top; // top spec
089: private String spec; // current spec
090: private String nextSpec; // next spec
091: private String scriptName; // script name equivalent of top, if != top
092: private boolean isSpecLocale; // TRUE if spec is a locale
093: private boolean isNextLocale; // TRUE if nextSpec is a locale
094: private ICUResourceBundle res;
095:
096: public Spec(String theSpec) {
097: top = theSpec;
098: spec = null;
099: scriptName = null;
100: try {
101: // Canonicalize script name. If top is a script name then
102: // script != UScript.INVALID_CODE.
103: int script = UScript.getCodeFromName(top);
104:
105: // Canonicalize script name -or- do locale->script mapping
106: int[] s = UScript.getCode(top);
107: if (s != null) {
108: scriptName = UScript.getName(s[0]);
109: // If the script name is the same as top then it's redundant
110: if (scriptName.equalsIgnoreCase(top)) {
111: scriptName = null;
112: }
113: }
114:
115: isSpecLocale = false;
116: res = null;
117: // If 'top' is not a script name, try a locale lookup
118: if (script == UScript.INVALID_CODE) {
119: Locale toploc = LocaleUtility
120: .getLocaleFromName(top);
121: res = (ICUResourceBundle) UResourceBundle
122: .getBundleInstance(
123: ICUResourceBundle.ICU_TRANSLIT_BASE_NAME,
124: toploc);
125: // Make sure we got the bundle we wanted; otherwise, don't use it
126: if (res != null
127: && LocaleUtility.isFallbackOf(res
128: .getULocale().toString(), top)) {
129: isSpecLocale = true;
130: }
131: }
132: } catch (MissingResourceException e) {
133: scriptName = null;
134: }
135: // assert(spec != top);
136: reset();
137: }
138:
139: public boolean hasFallback() {
140: return nextSpec != null;
141: }
142:
143: public void reset() {
144: if (spec != top) { // [sic] pointer comparison
145: spec = top;
146: isSpecLocale = (res != null);
147: setupNext();
148: }
149: }
150:
151: private void setupNext() {
152: isNextLocale = false;
153: if (isSpecLocale) {
154: nextSpec = spec;
155: int i = nextSpec.lastIndexOf(LOCALE_SEP);
156: // If i == 0 then we have _FOO, so we fall through
157: // to the scriptName.
158: if (i > 0) {
159: nextSpec = spec.substring(0, i);
160: isNextLocale = true;
161: } else {
162: nextSpec = scriptName; // scriptName may be null
163: }
164: } else {
165: // Fallback to the script, which may be null
166: if (nextSpec != scriptName) {
167: nextSpec = scriptName;
168: } else {
169: nextSpec = null;
170: }
171: }
172: }
173:
174: // Protocol:
175: // for(String& s(spec.get());
176: // spec.hasFallback(); s(spec.next())) { ...
177:
178: public String next() {
179: spec = nextSpec;
180: isSpecLocale = isNextLocale;
181: setupNext();
182: return spec;
183: }
184:
185: public String get() {
186: return spec;
187: }
188:
189: public boolean isLocale() {
190: return isSpecLocale;
191: }
192:
193: /**
194: * Return the ResourceBundle for this spec, at the current
195: * level of iteration. The level of iteration goes from
196: * aa_BB_CCC to aa_BB to aa. If the bundle does not
197: * correspond to the current level of iteration, return null.
198: * If isLocale() is false, always return null.
199: */
200: public ResourceBundle getBundle() {
201: if (res != null && res.getULocale().toString().equals(spec)) {
202: return res;
203: }
204: return null;
205: }
206:
207: public String getTop() {
208: return top;
209: }
210: }
211:
212: //----------------------------------------------------------------------
213: // Entry classes
214: //----------------------------------------------------------------------
215:
216: static class ResourceEntry {
217: public String resource;
218: public String encoding;
219: public int direction;
220:
221: public ResourceEntry(String n, String enc, int d) {
222: resource = n;
223: encoding = enc;
224: direction = d;
225: }
226: }
227:
228: // An entry representing a rule in a locale resource bundle
229: static class LocaleEntry {
230: public String rule;
231: public int direction;
232:
233: public LocaleEntry(String r, int d) {
234: rule = r;
235: direction = d;
236: }
237: }
238:
239: static class AliasEntry {
240: public String alias;
241:
242: public AliasEntry(String a) {
243: alias = a;
244: }
245: }
246:
247: static class CompoundRBTEntry {
248: private String ID;
249: private Vector idBlockVector;
250: private Vector dataVector;
251: private UnicodeSet compoundFilter;
252:
253: public CompoundRBTEntry(String theID, Vector theIDBlockVector,
254: Vector theDataVector, UnicodeSet theCompoundFilter) {
255: ID = theID;
256: idBlockVector = theIDBlockVector;
257: dataVector = theDataVector;
258: compoundFilter = theCompoundFilter;
259: }
260:
261: public Transliterator getInstance() {
262: Vector transliterators = new Vector();
263: int passNumber = 1;
264:
265: int limit = Math.max(idBlockVector.size(), dataVector
266: .size());
267: for (int i = 0; i < limit; i++) {
268: if (i < idBlockVector.size()) {
269: String idBlock = (String) idBlockVector.get(i);
270: if (idBlock.length() > 0)
271: transliterators.add(Transliterator
272: .getInstance(idBlock));
273: }
274: if (i < dataVector.size()) {
275: RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) dataVector
276: .get(i);
277: transliterators.add(new RuleBasedTransliterator(
278: "%Pass" + passNumber++, data, null));
279: }
280: }
281:
282: Transliterator t = new CompoundTransliterator(
283: transliterators, passNumber - 1);
284: t.setID(ID);
285: if (compoundFilter != null) {
286: t.setFilter(compoundFilter);
287: }
288: return t;
289: }
290: }
291:
292: //----------------------------------------------------------------------
293: // class TransliteratorRegistry: Basic public API
294: //----------------------------------------------------------------------
295:
296: public TransliteratorRegistry() {
297: registry = new Hashtable();
298: specDAG = new Hashtable();
299: availableIDs = new Vector();
300: }
301:
302: /**
303: * Given a simple ID (forward direction, no inline filter, not
304: * compound) attempt to instantiate it from the registry. Return
305: * 0 on failure.
306: *
307: * Return a non-empty aliasReturn value if the ID points to an alias.
308: * We cannot instantiate it ourselves because the alias may contain
309: * filters or compounds, which we do not understand. Caller should
310: * make aliasReturn empty before calling.
311: */
312: public Transliterator get(String ID, StringBuffer aliasReturn) {
313: Object[] entry = find(ID);
314: return (entry == null) ? null : instantiateEntry(ID, entry,
315: aliasReturn);
316: }
317:
318: /**
319: * Register a class. This adds an entry to the
320: * dynamic store, or replaces an existing entry. Any entry in the
321: * underlying static locale resource store is masked.
322: */
323: public void put(String ID, Class transliteratorSubclass,
324: boolean visible) {
325: registerEntry(ID, transliteratorSubclass, visible);
326: }
327:
328: /**
329: * Register an ID and a factory function pointer. This adds an
330: * entry to the dynamic store, or replaces an existing entry. Any
331: * entry in the underlying static locale resource store is masked.
332: */
333: public void put(String ID, Transliterator.Factory factory,
334: boolean visible) {
335: registerEntry(ID, factory, visible);
336: }
337:
338: /**
339: * Register an ID and a resource name. This adds an entry to the
340: * dynamic store, or replaces an existing entry. Any entry in the
341: * underlying static locale resource store is masked.
342: */
343: public void put(String ID, String resourceName, String encoding,
344: int dir, boolean visible) {
345: registerEntry(ID,
346: new ResourceEntry(resourceName, encoding, dir), visible);
347: }
348:
349: /**
350: * Register an ID and an alias ID. This adds an entry to the
351: * dynamic store, or replaces an existing entry. Any entry in the
352: * underlying static locale resource store is masked.
353: */
354: public void put(String ID, String alias, boolean visible) {
355: registerEntry(ID, new AliasEntry(alias), visible);
356: }
357:
358: /**
359: * Register an ID and a Transliterator object. This adds an entry
360: * to the dynamic store, or replaces an existing entry. Any entry
361: * in the underlying static locale resource store is masked.
362: */
363: public void put(String ID, Transliterator trans, boolean visible) {
364: registerEntry(ID, trans, visible);
365: }
366:
367: /**
368: * Unregister an ID. This removes an entry from the dynamic store
369: * if there is one. The static locale resource store is
370: * unaffected.
371: */
372: public void remove(String ID) {
373: String[] stv = TransliteratorIDParser.IDtoSTV(ID);
374: // Only need to do this if ID.indexOf('-') < 0
375: String id = TransliteratorIDParser.STVtoID(stv[0], stv[1],
376: stv[2]);
377: registry.remove(new CaseInsensitiveString(id));
378: removeSTV(stv[0], stv[1], stv[2]);
379: availableIDs.removeElement(new CaseInsensitiveString(id));
380: }
381:
382: //----------------------------------------------------------------------
383: // class TransliteratorRegistry: Public ID and spec management
384: //----------------------------------------------------------------------
385:
386: /**
387: * An internal class that adapts an enumeration over
388: * CaseInsensitiveStrings to an enumeration over Strings.
389: */
390: private static class IDEnumeration implements Enumeration {
391: Enumeration en;
392:
393: public IDEnumeration(Enumeration e) {
394: en = e;
395: }
396:
397: public boolean hasMoreElements() {
398: return en != null && en.hasMoreElements();
399: }
400:
401: public Object nextElement() {
402: return ((CaseInsensitiveString) en.nextElement())
403: .getString();
404: }
405: }
406:
407: /**
408: * Returns an enumeration over the programmatic names of visible
409: * registered transliterators.
410: *
411: * @return An <code>Enumeration</code> over <code>String</code> objects
412: */
413: public Enumeration getAvailableIDs() {
414: // Since the cache contains CaseInsensitiveString objects, but
415: // the caller expects Strings, we have to use an intermediary.
416: return new IDEnumeration(availableIDs.elements());
417: }
418:
419: /**
420: * Returns an enumeration over all visible source names.
421: *
422: * @return An <code>Enumeration</code> over <code>String</code> objects
423: */
424: public Enumeration getAvailableSources() {
425: return new IDEnumeration(specDAG.keys());
426: }
427:
428: /**
429: * Returns an enumeration over visible target names for the given
430: * source.
431: *
432: * @return An <code>Enumeration</code> over <code>String</code> objects
433: */
434: public Enumeration getAvailableTargets(String source) {
435: CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
436: Hashtable targets = (Hashtable) specDAG.get(cisrc);
437: if (targets == null) {
438: return new IDEnumeration(null);
439: }
440: return new IDEnumeration(targets.keys());
441: }
442:
443: /**
444: * Returns an enumeration over visible variant names for the given
445: * source and target.
446: *
447: * @return An <code>Enumeration</code> over <code>String</code> objects
448: */
449: public Enumeration getAvailableVariants(String source, String target) {
450: CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
451: CaseInsensitiveString citrg = new CaseInsensitiveString(target);
452: Hashtable targets = (Hashtable) specDAG.get(cisrc);
453: if (targets == null) {
454: return new IDEnumeration(null);
455: }
456: Vector variants = (Vector) targets.get(citrg);
457: if (variants == null) {
458: return new IDEnumeration(null);
459: }
460: return new IDEnumeration(variants.elements());
461: }
462:
463: //----------------------------------------------------------------------
464: // class TransliteratorRegistry: internal
465: //----------------------------------------------------------------------
466:
467: /**
468: * Convenience method. Calls 6-arg registerEntry().
469: */
470: private void registerEntry(String source, String target,
471: String variant, Object entry, boolean visible) {
472: String s = source;
473: if (s.length() == 0) {
474: s = ANY;
475: }
476: String ID = TransliteratorIDParser.STVtoID(source, target,
477: variant);
478: registerEntry(ID, s, target, variant, entry, visible);
479: }
480:
481: /**
482: * Convenience method. Calls 6-arg registerEntry().
483: */
484: private void registerEntry(String ID, Object entry, boolean visible) {
485: String[] stv = TransliteratorIDParser.IDtoSTV(ID);
486: // Only need to do this if ID.indexOf('-') < 0
487: String id = TransliteratorIDParser.STVtoID(stv[0], stv[1],
488: stv[2]);
489: registerEntry(id, stv[0], stv[1], stv[2], entry, visible);
490: }
491:
492: /**
493: * Register an entry object (adopted) with the given ID, source,
494: * target, and variant strings.
495: */
496: private void registerEntry(String ID, String source, String target,
497: String variant, Object entry, boolean visible) {
498: CaseInsensitiveString ciID = new CaseInsensitiveString(ID);
499:
500: // Store the entry within an array so it can be modified later
501: if (!(entry instanceof Object[])) {
502: entry = new Object[] { entry };
503: }
504:
505: registry.put(ciID, entry);
506: if (visible) {
507: registerSTV(source, target, variant);
508: if (!availableIDs.contains(ciID)) {
509: availableIDs.addElement(ciID);
510: }
511: } else {
512: removeSTV(source, target, variant);
513: availableIDs.removeElement(ciID);
514: }
515: }
516:
517: /**
518: * Register a source-target/variant in the specDAG. Variant may be
519: * empty, but source and target must not be. If variant is empty then
520: * the special variant NO_VARIANT is stored in slot zero of the
521: * UVector of variants.
522: */
523: private void registerSTV(String source, String target,
524: String variant) {
525: // assert(source.length() > 0);
526: // assert(target.length() > 0);
527: CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
528: CaseInsensitiveString citrg = new CaseInsensitiveString(target);
529: CaseInsensitiveString civar = new CaseInsensitiveString(variant);
530: Hashtable targets = (Hashtable) specDAG.get(cisrc);
531: if (targets == null) {
532: targets = new Hashtable();
533: specDAG.put(cisrc, targets);
534: }
535: Vector variants = (Vector) targets.get(citrg);
536: if (variants == null) {
537: variants = new Vector();
538: targets.put(citrg, variants);
539: }
540: // assert(NO_VARIANT == "");
541: // We add the variant string. If it is the special "no variant"
542: // string, that is, the empty string, we add it at position zero.
543: if (!variants.contains(civar)) {
544: if (variant.length() > 0) {
545: variants.addElement(civar);
546: } else {
547: variants.insertElementAt(civar, 0);
548: }
549: }
550: }
551:
552: /**
553: * Remove a source-target/variant from the specDAG.
554: */
555: private void removeSTV(String source, String target, String variant) {
556: // assert(source.length() > 0);
557: // assert(target.length() > 0);
558: CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
559: CaseInsensitiveString citrg = new CaseInsensitiveString(target);
560: CaseInsensitiveString civar = new CaseInsensitiveString(variant);
561: Hashtable targets = (Hashtable) specDAG.get(cisrc);
562: if (targets == null) {
563: return; // should never happen for valid s-t/v
564: }
565: Vector variants = (Vector) targets.get(citrg);
566: if (variants == null) {
567: return; // should never happen for valid s-t/v
568: }
569: variants.removeElement(civar);
570: if (variants.size() == 0) {
571: targets.remove(citrg); // should delete variants
572: if (targets.size() == 0) {
573: specDAG.remove(cisrc); // should delete targets
574: }
575: }
576: }
577:
578: private static final boolean DEBUG = false;
579:
580: /**
581: * Attempt to find a source-target/variant in the dynamic registry
582: * store. Return 0 on failure.
583: */
584: private Object[] findInDynamicStore(Spec src, Spec trg,
585: String variant) {
586: String ID = TransliteratorIDParser.STVtoID(src.get(),
587: trg.get(), variant);
588: if (DEBUG) {
589: System.out
590: .println("TransliteratorRegistry.findInDynamicStore:"
591: + ID);
592: }
593: return (Object[]) registry.get(new CaseInsensitiveString(ID));
594: }
595:
596: /**
597: * Attempt to find a source-target/variant in the static locale
598: * resource store. Do not perform fallback. Return 0 on failure.
599: *
600: * On success, create a new entry object, register it in the dynamic
601: * store, and return a pointer to it, but do not make it public --
602: * just because someone requested something, we do not expand the
603: * available ID list (or spec DAG).
604: */
605: private Object[] findInStaticStore(Spec src, Spec trg,
606: String variant) {
607: if (DEBUG) {
608: String ID = TransliteratorIDParser.STVtoID(src.get(), trg
609: .get(), variant);
610: System.out
611: .println("TransliteratorRegistry.findInStaticStore:"
612: + ID);
613: }
614: Object[] entry = null;
615: if (src.isLocale()) {
616: entry = findInBundle(src, trg, variant,
617: Transliterator.FORWARD);
618: } else if (trg.isLocale()) {
619: entry = findInBundle(trg, src, variant,
620: Transliterator.REVERSE);
621: }
622:
623: // If we found an entry, store it in the Hashtable for next
624: // time.
625: if (entry != null) {
626: registerEntry(src.getTop(), trg.getTop(), variant, entry,
627: false);
628: }
629:
630: return entry;
631: }
632:
633: /**
634: * Attempt to find an entry in a single resource bundle. This is
635: * a one-sided lookup. findInStaticStore() performs up to two such
636: * lookups, one for the source, and one for the target.
637: *
638: * Do not perform fallback. Return 0 on failure.
639: *
640: * On success, create a new Entry object, populate it, and return it.
641: * The caller owns the returned object.
642: */
643: private Object[] findInBundle(Spec specToOpen, Spec specToFind,
644: String variant, int direction) {
645: // assert(specToOpen.isLocale());
646: ResourceBundle res = specToOpen.getBundle();
647:
648: if (res == null) {
649: // This means that the bundle's locale does not match
650: // the current level of iteration for the spec.
651: return null;
652: }
653:
654: for (int pass = 0; pass < 2; ++pass) {
655: StringBuffer tag = new StringBuffer();
656: // First try either TransliteratorTo_xxx or
657: // TransliterateFrom_xxx, then try the bidirectional
658: // Transliterate_xxx. This precedence order is arbitrary
659: // but must be consistent and documented.
660: if (pass == 0) {
661: tag
662: .append(direction == Transliterator.FORWARD ? "TransliterateTo"
663: : "TransliterateFrom");
664: } else {
665: tag.append("Transliterate");
666: }
667: tag.append(specToFind.get().toUpperCase());
668:
669: try {
670: // The Transliterate*_xxx resource is an array of
671: // strings of the format { <v0>, <r0>, ... }. Each
672: // <vi> is a variant name, and each <ri> is a rule.
673: String[] subres = res.getStringArray(tag.toString());
674:
675: // assert(subres != null);
676: // assert(subres.length % 2 == 0);
677: int i = 0;
678: if (variant.length() != 0) {
679: for (i = 0; i < subres.length; i += 2) {
680: if (subres[i].equalsIgnoreCase(variant)) {
681: break;
682: }
683: }
684: }
685:
686: if (i < subres.length) {
687: // We have a match, or there is no variant and i == 0.
688: // We have succeeded in loading a string from the
689: // locale resources. Return the rule string which
690: // will itself become the registry entry.
691:
692: // The direction is always forward for the
693: // TransliterateTo_xxx and TransliterateFrom_xxx
694: // items; those are unidirectional forward rules.
695: // For the bidirectional Transliterate_xxx items,
696: // the direction is the value passed in to this
697: // function.
698: int dir = (pass == 0) ? Transliterator.FORWARD
699: : direction;
700: return new Object[] { new LocaleEntry(
701: subres[i + 1], dir) };
702: }
703:
704: } catch (MissingResourceException e) {
705: if (DEBUG)
706: System.out.println("missing resource: " + e);
707: }
708: }
709:
710: // If we get here we had a missing resource exception or we
711: // failed to find a desired variant.
712: return null;
713: }
714:
715: /**
716: * Convenience method. Calls 3-arg find().
717: */
718: private Object[] find(String ID) {
719: String[] stv = TransliteratorIDParser.IDtoSTV(ID);
720: return find(stv[0], stv[1], stv[2]);
721: }
722:
723: /**
724: * Top-level find method. Attempt to find a source-target/variant in
725: * either the dynamic or the static (locale resource) store. Perform
726: * fallback.
727: *
728: * Lookup sequence for ss_SS_SSS-tt_TT_TTT/v:
729: *
730: * ss_SS_SSS-tt_TT_TTT/v -- in hashtable
731: * ss_SS_SSS-tt_TT_TTT/v -- in ss_SS_SSS (no fallback)
732: *
733: * repeat with t = tt_TT_TTT, tt_TT, tt, and tscript
734: *
735: * ss_SS_SSS-t/*
736: * ss_SS-t/*
737: * ss-t/*
738: * sscript-t/*
739: *
740: * Here * matches the first variant listed.
741: *
742: * Caller does NOT own returned object. Return 0 on failure.
743: */
744: private Object[] find(String source, String target, String variant) {
745:
746: Spec src = new Spec(source);
747: Spec trg = new Spec(target);
748: Object[] entry = null;
749:
750: if (variant.length() != 0) {
751:
752: // Seek exact match in hashtable
753: entry = findInDynamicStore(src, trg, variant);
754: if (entry != null) {
755: return entry;
756: }
757:
758: // Seek exact match in locale resources
759: entry = findInStaticStore(src, trg, variant);
760: if (entry != null) {
761: return entry;
762: }
763: }
764:
765: for (;;) {
766: src.reset();
767: for (;;) {
768: // Seek match in hashtable
769: entry = findInDynamicStore(src, trg, NO_VARIANT);
770: if (entry != null) {
771: return entry;
772: }
773:
774: // Seek match in locale resources
775: entry = findInStaticStore(src, trg, NO_VARIANT);
776: if (entry != null) {
777: return entry;
778: }
779: if (!src.hasFallback()) {
780: break;
781: }
782: src.next();
783: }
784: if (!trg.hasFallback()) {
785: break;
786: }
787: trg.next();
788: }
789:
790: return null;
791: }
792:
793: /**
794: * Given an Entry object, instantiate it. Caller owns result. Return
795: * 0 on failure.
796: *
797: * Return a non-empty aliasReturn value if the ID points to an alias.
798: * We cannot instantiate it ourselves because the alias may contain
799: * filters or compounds, which we do not understand. Caller should
800: * make aliasReturn empty before calling.
801: *
802: * The entry object is assumed to reside in the dynamic store. It may be
803: * modified.
804: */
805: private Transliterator instantiateEntry(String ID,
806: Object[] entryWrapper, StringBuffer aliasReturn) {
807: // We actually modify the entry object in some cases. If it
808: // is a string, we may partially parse it and turn it into a
809: // more processed precursor. This makes the next
810: // instantiation faster and allows sharing of immutable
811: // components like the RuleBasedTransliterator.Data objects.
812: // For this reason, the entry object is an Object[] of length
813: // 1.
814:
815: for (;;) {
816: Object entry = entryWrapper[0];
817:
818: if (entry instanceof RuleBasedTransliterator.Data) {
819: RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) entry;
820: return new RuleBasedTransliterator(ID, data, null);
821: } else if (entry instanceof Class) {
822: try {
823: return (Transliterator) ((Class) entry)
824: .newInstance();
825: } catch (InstantiationException e) {
826: } catch (IllegalAccessException e2) {
827: }
828: return null;
829: } else if (entry instanceof AliasEntry) {
830: aliasReturn.append(((AliasEntry) entry).alias);
831: return null;
832: } else if (entry instanceof Transliterator.Factory) {
833: return ((Transliterator.Factory) entry).getInstance(ID);
834: } else if (entry instanceof CompoundRBTEntry) {
835: return ((CompoundRBTEntry) entry).getInstance();
836: } else if (entry instanceof Transliterator) {
837: return (Transliterator) entry;
838: }
839:
840: // At this point entry type must be either RULES_FORWARD or
841: // RULES_REVERSE. We process the rule data into a
842: // TransliteratorRuleData object, and possibly also into an
843: // .id header and/or footer. Then we modify the registry with
844: // the parsed data and retry.
845:
846: TransliteratorParser parser = new TransliteratorParser();
847:
848: try {
849:
850: ResourceEntry re = (ResourceEntry) entry;
851: parser.parse(re.resource, re.direction);
852:
853: } catch (ClassCastException e) {
854: // If we pull a rule from a locale resource bundle it will
855: // be a LocaleEntry.
856: LocaleEntry le = (LocaleEntry) entry;
857: parser.parse(le.rule, le.direction);
858: }
859:
860: // Reset entry to something that we process at the
861: // top of the loop, then loop back to the top. As long as we
862: // do this, we only loop through twice at most.
863: // NOTE: The logic here matches that in
864: // Transliterator.createFromRules().
865: if (parser.idBlockVector.size() == 0
866: && parser.dataVector.size() == 0) {
867: // No idBlock, no data -- this is just an
868: // alias for Null
869: entryWrapper[0] = new AliasEntry(NullTransliterator._ID);
870: } else if (parser.idBlockVector.size() == 0
871: && parser.dataVector.size() == 1) {
872: // No idBlock, data != 0 -- this is an
873: // ordinary RBT_DATA
874: entryWrapper[0] = parser.dataVector.get(0);
875: } else if (parser.idBlockVector.size() == 1
876: && parser.dataVector.size() == 0) {
877: // idBlock, no data -- this is an alias. The ID has
878: // been munged from reverse into forward mode, if
879: // necessary, so instantiate the ID in the forward
880: // direction.
881: if (parser.compoundFilter != null)
882: entryWrapper[0] = new AliasEntry(
883: parser.compoundFilter.toPattern(false)
884: + ";"
885: + (String) parser.idBlockVector
886: .get(0));
887: else
888: entryWrapper[0] = new AliasEntry(
889: (String) parser.idBlockVector.get(0));
890: } else {
891: entryWrapper[0] = new CompoundRBTEntry(ID,
892: parser.idBlockVector, parser.dataVector,
893: parser.compoundFilter);
894: }
895: }
896: }
897: }
898:
899: //eof
|