KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > text > TransliteratorRegistry


1 /*
2 **********************************************************************
3 * Copyright (c) 2001-2005, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
6 * Date Name Description
7 * 08/19/2001 aliu Creation.
8 **********************************************************************
9 */

10
11 package com.ibm.icu.text;
12
13 //import com.ibm.icu.impl.ICULocaleData;
14
import com.ibm.icu.impl.ICUResourceBundle;
15 import com.ibm.icu.impl.LocaleUtility;
16 import com.ibm.icu.lang.UScript;
17 import com.ibm.icu.util.CaseInsensitiveString;
18 import com.ibm.icu.util.UResourceBundle;
19
20 import java.util.Enumeration JavaDoc;
21 import java.util.Hashtable JavaDoc;
22 import java.util.Locale JavaDoc;
23 import java.util.MissingResourceException JavaDoc;
24 import java.util.ResourceBundle JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 class TransliteratorRegistry {
28
29     // char constants
30
private static final char LOCALE_SEP = '_';
31
32     // String constants
33
private static final String JavaDoc NO_VARIANT = ""; // empty string
34
private static final String JavaDoc ANY = "Any";
35
36     /**
37      * Dynamic registry mapping full IDs to Entry objects. This
38      * contains both public and internal entities. The visibility is
39      * controlled by whether an entry is listed in availableIDs and
40      * specDAG or not.
41      *
42      * Keys are CaseInsensitiveString objects.
43      * Values are objects of class Class (subclass of Transliterator),
44      * RuleBasedTransliterator.Data, Transliterator.Factory, or one
45      * of the entry classes defined here (AliasEntry or ResourceEntry).
46      */

47     private Hashtable JavaDoc registry;
48
49     /**
50      * DAG of visible IDs by spec. Hashtable: source => (Hashtable:
51      * target => (Vector: variant)) The Vector of variants is never
52      * empty. For a source-target with no variant, the special
53      * variant NO_VARIANT (the empty string) is stored in slot zero of
54      * the UVector.
55      *
56      * Keys are CaseInsensitiveString objects.
57      * Values are Hashtable of (CaseInsensitiveString -> Vector of
58      * CaseInsensitiveString)
59      */

60     private Hashtable JavaDoc specDAG;
61
62     /**
63      * Vector of public full IDs (CaseInsensitiveString objects).
64      */

65     private Vector JavaDoc availableIDs;
66
67     //----------------------------------------------------------------------
68
// class Spec
69
//----------------------------------------------------------------------
70

71     /**
72      * A Spec is a string specifying either a source or a target. In more
73      * general terms, it may also specify a variant, but we only use the
74      * Spec class for sources and targets.
75      *
76      * A Spec may be a locale or a script. If it is a locale, it has a
77      * fallback chain that goes xx_YY_ZZZ -> xx_YY -> xx -> ssss, where
78      * ssss is the script mapping of xx_YY_ZZZ. The Spec API methods
79      * hasFallback(), next(), and reset() iterate over this fallback
80      * sequence.
81      *
82      * The Spec class canonicalizes itself, so the locale is put into
83      * canonical form, or the script is transformed from an abbreviation
84      * to a full name.
85      */

86     static class Spec {
87
88         private String JavaDoc top; // top spec
89
private String JavaDoc spec; // current spec
90
private String JavaDoc nextSpec; // next spec
91
private String JavaDoc scriptName; // script name equivalent of top, if != top
92
private boolean isSpecLocale; // TRUE if spec is a locale
93
private boolean isNextLocale; // TRUE if nextSpec is a locale
94
private ICUResourceBundle res;
95
96         public Spec(String JavaDoc theSpec) {
97             top = theSpec;
98             spec = null;
99             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 JavaDoc toploc = LocaleUtility.getLocaleFromName(top);
120                     res = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_TRANSLIT_BASE_NAME,toploc);
121                     // Make sure we got the bundle we wanted; otherwise, don't use it
122
if (res!=null && LocaleUtility.isFallbackOf(res.getULocale().toString(), top)) {
123                         isSpecLocale = true;
124                     }
125                 }
126             }catch(MissingResourceException JavaDoc e){
127                 scriptName = null;
128             }
129             // assert(spec != top);
130
reset();
131         }
132
133         public boolean hasFallback() {
134             return nextSpec != null;
135         }
136
137         public void reset() {
138             if (spec != top) { // [sic] pointer comparison
139
spec = top;
140                 isSpecLocale = (res != null);
141                 setupNext();
142             }
143         }
144
145         private void setupNext() {
146             isNextLocale = false;
147             if (isSpecLocale) {
148                 nextSpec = spec;
149                 int i = nextSpec.lastIndexOf(LOCALE_SEP);
150                 // If i == 0 then we have _FOO, so we fall through
151
// to the scriptName.
152
if (i > 0) {
153                     nextSpec = spec.substring(0, i);
154                     isNextLocale = true;
155                 } else {
156                     nextSpec = scriptName; // scriptName may be null
157
}
158             } else {
159                 // Fallback to the script, which may be null
160
if (nextSpec != scriptName) {
161                     nextSpec = scriptName;
162                 } else {
163                     nextSpec = null;
164                 }
165             }
166         }
167
168         // Protocol:
169
// for(String& s(spec.get());
170
// spec.hasFallback(); s(spec.next())) { ...
171

172         public String JavaDoc next() {
173             spec = nextSpec;
174             isSpecLocale = isNextLocale;
175             setupNext();
176             return spec;
177         }
178
179         public String JavaDoc get() {
180             return spec;
181         }
182
183         public boolean isLocale() {
184             return isSpecLocale;
185         }
186
187         /**
188          * Return the ResourceBundle for this spec, at the current
189          * level of iteration. The level of iteration goes from
190          * aa_BB_CCC to aa_BB to aa. If the bundle does not
191          * correspond to the current level of iteration, return null.
192          * If isLocale() is false, always return null.
193          */

194         public ResourceBundle getBundle() {
195             if (res != null &&
196                 res.getULocale().toString().equals(spec)) {
197                 return res;
198             }
199             return null;
200         }
201
202         public String JavaDoc getTop() {
203             return top;
204         }
205     }
206
207     //----------------------------------------------------------------------
208
// Entry classes
209
//----------------------------------------------------------------------
210

211     static class ResourceEntry {
212         public String JavaDoc resource;
213         public String JavaDoc encoding;
214         public int direction;
215         public ResourceEntry(String JavaDoc n, String JavaDoc enc, int d) {
216             resource = n;
217             encoding = enc;
218             direction = d;
219         }
220     }
221
222     // An entry representing a rule in a locale resource bundle
223
static class LocaleEntry {
224         public String JavaDoc rule;
225         public int direction;
226         public LocaleEntry(String JavaDoc r, int d) {
227             rule = r;
228             direction = d;
229         }
230     }
231
232     static class AliasEntry {
233         public String JavaDoc alias;
234         public AliasEntry(String JavaDoc a) {
235             alias = a;
236         }
237     }
238
239     static class CompoundRBTEntry {
240         private String JavaDoc ID;
241         private Vector JavaDoc idBlockVector;
242         private Vector JavaDoc dataVector;
243         private UnicodeSet compoundFilter;
244
245         public CompoundRBTEntry(String JavaDoc theID, Vector JavaDoc theIDBlockVector,
246                                 Vector JavaDoc theDataVector,
247                                 UnicodeSet theCompoundFilter) {
248             ID = theID;
249             idBlockVector = theIDBlockVector;
250             dataVector = theDataVector;
251             compoundFilter = theCompoundFilter;
252         }
253
254         public Transliterator getInstance() {
255             Vector JavaDoc transliterators = new Vector JavaDoc();
256             int passNumber = 1;
257
258             int limit = Math.max(idBlockVector.size(), dataVector.size());
259             for (int i = 0; i < limit; i++) {
260                 if (i < idBlockVector.size()) {
261                     String JavaDoc idBlock = (String JavaDoc)idBlockVector.get(i);
262                     if (idBlock.length() > 0)
263                         transliterators.add(Transliterator.getInstance(idBlock));
264                 }
265                 if (i < dataVector.size()) {
266                     RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data)dataVector.get(i);
267                     transliterators.add(new RuleBasedTransliterator("%Pass" + passNumber++, data, null));
268                 }
269             }
270
271             Transliterator t = new CompoundTransliterator(transliterators, passNumber - 1);
272             t.setID(ID);
273             if (compoundFilter != null) {
274                 t.setFilter(compoundFilter);
275             }
276             return t;
277         }
278     }
279
280     //----------------------------------------------------------------------
281
// class TransliteratorRegistry: Basic public API
282
//----------------------------------------------------------------------
283

284     public TransliteratorRegistry() {
285         registry = new Hashtable JavaDoc();
286         specDAG = new Hashtable JavaDoc();
287         availableIDs = new Vector JavaDoc();
288     }
289
290     /**
291      * Given a simple ID (forward direction, no inline filter, not
292      * compound) attempt to instantiate it from the registry. Return
293      * 0 on failure.
294      *
295      * Return a non-empty aliasReturn value if the ID points to an alias.
296      * We cannot instantiate it ourselves because the alias may contain
297      * filters or compounds, which we do not understand. Caller should
298      * make aliasReturn empty before calling.
299      */

300     public Transliterator get(String JavaDoc ID,
301                               StringBuffer JavaDoc aliasReturn) {
302         Object JavaDoc[] entry = find(ID);
303         return (entry == null) ? null
304             : instantiateEntry(ID, entry, aliasReturn);
305     }
306
307     /**
308      * Register a class. This adds an entry to the
309      * dynamic store, or replaces an existing entry. Any entry in the
310      * underlying static locale resource store is masked.
311      */

312     public void put(String JavaDoc ID,
313                     Class JavaDoc transliteratorSubclass,
314                     boolean visible) {
315         registerEntry(ID, transliteratorSubclass, visible);
316     }
317
318     /**
319      * Register an ID and a factory function pointer. This adds an
320      * entry to the dynamic store, or replaces an existing entry. Any
321      * entry in the underlying static locale resource store is masked.
322      */

323     public void put(String JavaDoc ID,
324                     Transliterator.Factory factory,
325                     boolean visible) {
326         registerEntry(ID, factory, visible);
327     }
328
329     /**
330      * Register an ID and a resource name. This adds an entry to the
331      * dynamic store, or replaces an existing entry. Any entry in the
332      * underlying static locale resource store is masked.
333      */

334     public void put(String JavaDoc ID,
335                     String JavaDoc resourceName,
336                     String JavaDoc encoding,
337                     int dir,
338                     boolean visible) {
339         registerEntry(ID, new ResourceEntry(resourceName, encoding, dir), visible);
340     }
341
342     /**
343      * Register an ID and an alias ID. This adds an entry to the
344      * dynamic store, or replaces an existing entry. Any entry in the
345      * underlying static locale resource store is masked.
346      */

347     public void put(String JavaDoc ID,
348                     String JavaDoc alias,
349                     boolean visible) {
350         registerEntry(ID, new AliasEntry(alias), visible);
351     }
352
353     /**
354      * Register an ID and a Transliterator object. This adds an entry
355      * to the dynamic store, or replaces an existing entry. Any entry
356      * in the underlying static locale resource store is masked.
357      */

358     public void put(String JavaDoc ID,
359                     Transliterator trans,
360                     boolean visible) {
361         registerEntry(ID, trans, visible);
362     }
363
364     /**
365      * Unregister an ID. This removes an entry from the dynamic store
366      * if there is one. The static locale resource store is
367      * unaffected.
368      */

369     public void remove(String JavaDoc ID) {
370         String JavaDoc[] stv = TransliteratorIDParser.IDtoSTV(ID);
371         // Only need to do this if ID.indexOf('-') < 0
372
String JavaDoc id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
373         registry.remove(new CaseInsensitiveString(id));
374         removeSTV(stv[0], stv[1], stv[2]);
375         availableIDs.removeElement(new CaseInsensitiveString(id));
376     }
377
378     //----------------------------------------------------------------------
379
// class TransliteratorRegistry: Public ID and spec management
380
//----------------------------------------------------------------------
381

382     /**
383      * An internal class that adapts an enumeration over
384      * CaseInsensitiveStrings to an enumeration over Strings.
385      */

386     private static class IDEnumeration implements Enumeration JavaDoc {
387         Enumeration JavaDoc en;
388
389         public IDEnumeration(Enumeration JavaDoc e) {
390             en = e;
391         }
392
393         public boolean hasMoreElements() {
394             return en != null && en.hasMoreElements();
395         }
396
397         public Object JavaDoc nextElement() {
398             return ((CaseInsensitiveString) en.nextElement()).getString();
399         }
400     }
401
402     /**
403      * Returns an enumeration over the programmatic names of visible
404      * registered transliterators.
405      *
406      * @return An <code>Enumeration</code> over <code>String</code> objects
407      */

408     public Enumeration JavaDoc getAvailableIDs() {
409         // Since the cache contains CaseInsensitiveString objects, but
410
// the caller expects Strings, we have to use an intermediary.
411
return new IDEnumeration(availableIDs.elements());
412     }
413
414     /**
415      * Returns an enumeration over all visible source names.
416      *
417      * @return An <code>Enumeration</code> over <code>String</code> objects
418      */

419     public Enumeration JavaDoc getAvailableSources() {
420         return new IDEnumeration(specDAG.keys());
421     }
422
423     /**
424      * Returns an enumeration over visible target names for the given
425      * source.
426      *
427      * @return An <code>Enumeration</code> over <code>String</code> objects
428      */

429     public Enumeration JavaDoc getAvailableTargets(String JavaDoc source) {
430         CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
431         Hashtable JavaDoc targets = (Hashtable JavaDoc) specDAG.get(cisrc);
432         if (targets == null) {
433             return new IDEnumeration(null);
434         }
435         return new IDEnumeration(targets.keys());
436     }
437
438     /**
439      * Returns an enumeration over visible variant names for the given
440      * source and target.
441      *
442      * @return An <code>Enumeration</code> over <code>String</code> objects
443      */

444     public Enumeration JavaDoc getAvailableVariants(String JavaDoc source, String JavaDoc target) {
445         CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
446         CaseInsensitiveString citrg = new CaseInsensitiveString(target);
447         Hashtable JavaDoc targets = (Hashtable JavaDoc) specDAG.get(cisrc);
448         if (targets == null) {
449             return new IDEnumeration(null);
450         }
451         Vector JavaDoc variants = (Vector JavaDoc) targets.get(citrg);
452         if (variants == null) {
453             return new IDEnumeration(null);
454         }
455         return new IDEnumeration(variants.elements());
456     }
457
458     //----------------------------------------------------------------------
459
// class TransliteratorRegistry: internal
460
//----------------------------------------------------------------------
461

462     /**
463      * Convenience method. Calls 6-arg registerEntry().
464      */

465     private void registerEntry(String JavaDoc source,
466                                String JavaDoc target,
467                                String JavaDoc variant,
468                                Object JavaDoc entry,
469                                boolean visible) {
470         String JavaDoc s = source;
471         if (s.length() == 0) {
472             s = ANY;
473         }
474         String JavaDoc ID = TransliteratorIDParser.STVtoID(source, target, variant);
475         registerEntry(ID, s, target, variant, entry, visible);
476     }
477
478     /**
479      * Convenience method. Calls 6-arg registerEntry().
480      */

481     private void registerEntry(String JavaDoc ID,
482                                Object JavaDoc entry,
483                                boolean visible) {
484         String JavaDoc[] stv = TransliteratorIDParser.IDtoSTV(ID);
485         // Only need to do this if ID.indexOf('-') < 0
486
String JavaDoc id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
487         registerEntry(id, stv[0], stv[1], stv[2], entry, visible);
488     }
489
490     /**
491      * Register an entry object (adopted) with the given ID, source,
492      * target, and variant strings.
493      */

494     private void registerEntry(String JavaDoc ID,
495                                String JavaDoc source,
496                                String JavaDoc target,
497                                String JavaDoc variant,
498                                Object JavaDoc entry,
499                                boolean visible) {
500         CaseInsensitiveString ciID = new CaseInsensitiveString(ID);
501
502         // Store the entry within an array so it can be modified later
503
if (!(entry instanceof Object JavaDoc[])) {
504             entry = new Object JavaDoc[] { entry };
505         }
506
507         registry.put(ciID, entry);
508         if (visible) {
509             registerSTV(source, target, variant);
510             if (!availableIDs.contains(ciID)) {
511                 availableIDs.addElement(ciID);
512             }
513         } else {
514             removeSTV(source, target, variant);
515             availableIDs.removeElement(ciID);
516         }
517     }
518
519     /**
520      * Register a source-target/variant in the specDAG. Variant may be
521      * empty, but source and target must not be. If variant is empty then
522      * the special variant NO_VARIANT is stored in slot zero of the
523      * UVector of variants.
524      */

525     private void registerSTV(String JavaDoc source,
526                              String JavaDoc target,
527                              String JavaDoc variant) {
528         // assert(source.length() > 0);
529
// assert(target.length() > 0);
530
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
531         CaseInsensitiveString citrg = new CaseInsensitiveString(target);
532         CaseInsensitiveString civar = new CaseInsensitiveString(variant);
533         Hashtable JavaDoc targets = (Hashtable JavaDoc) specDAG.get(cisrc);
534         if (targets == null) {
535             targets = new Hashtable JavaDoc();
536             specDAG.put(cisrc, targets);
537         }
538         Vector JavaDoc variants = (Vector JavaDoc) targets.get(citrg);
539         if (variants == null) {
540             variants = new Vector JavaDoc();
541             targets.put(citrg, variants);
542         }
543         // assert(NO_VARIANT == "");
544
// We add the variant string. If it is the special "no variant"
545
// string, that is, the empty string, we add it at position zero.
546
if (!variants.contains(civar)) {
547             if (variant.length() > 0) {
548                 variants.addElement(civar);
549             } else {
550                 variants.insertElementAt(civar, 0);
551             }
552         }
553     }
554
555     /**
556      * Remove a source-target/variant from the specDAG.
557      */

558     private void removeSTV(String JavaDoc source,
559                            String JavaDoc target,
560                            String JavaDoc variant) {
561         // assert(source.length() > 0);
562
// assert(target.length() > 0);
563
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
564         CaseInsensitiveString citrg = new CaseInsensitiveString(target);
565         CaseInsensitiveString civar = new CaseInsensitiveString(variant);
566         Hashtable JavaDoc targets = (Hashtable JavaDoc) specDAG.get(cisrc);
567         if (targets == null) {
568             return; // should never happen for valid s-t/v
569
}
570         Vector JavaDoc variants = (Vector JavaDoc) targets.get(citrg);
571         if (variants == null) {
572             return; // should never happen for valid s-t/v
573
}
574         variants.removeElement(civar);
575         if (variants.size() == 0) {
576             targets.remove(citrg); // should delete variants
577
if (targets.size() == 0) {
578                 specDAG.remove(cisrc); // should delete targets
579
}
580         }
581     }
582
583     private static final boolean DEBUG = false;
584
585     /**
586      * Attempt to find a source-target/variant in the dynamic registry
587      * store. Return 0 on failure.
588      */

589     private Object JavaDoc[] findInDynamicStore(Spec src,
590                                       Spec trg,
591                                       String JavaDoc variant) {
592         String JavaDoc ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
593         if (DEBUG) {
594             System.out.println("TransliteratorRegistry.findInDynamicStore:" +
595                                ID);
596         }
597         return (Object JavaDoc[]) registry.get(new CaseInsensitiveString(ID));
598     }
599
600     /**
601      * Attempt to find a source-target/variant in the static locale
602      * resource store. Do not perform fallback. Return 0 on failure.
603      *
604      * On success, create a new entry object, register it in the dynamic
605      * store, and return a pointer to it, but do not make it public --
606      * just because someone requested something, we do not expand the
607      * available ID list (or spec DAG).
608      */

609     private Object JavaDoc[] findInStaticStore(Spec src,
610                                      Spec trg,
611                                      String JavaDoc variant) {
612         if (DEBUG) {
613             String JavaDoc ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
614             System.out.println("TransliteratorRegistry.findInStaticStore:" +
615                                ID);
616         }
617         Object JavaDoc[] entry = null;
618         if (src.isLocale()) {
619             entry = findInBundle(src, trg, variant, Transliterator.FORWARD);
620         } else if (trg.isLocale()) {
621             entry = findInBundle(trg, src, variant, Transliterator.REVERSE);
622         }
623
624         // If we found an entry, store it in the Hashtable for next
625
// time.
626
if (entry != null) {
627             registerEntry(src.getTop(), trg.getTop(), variant, entry, 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 JavaDoc[] findInBundle(Spec specToOpen,
644                                   Spec specToFind,
645                                   String JavaDoc variant,
646                                   int direction) {
647         // assert(specToOpen.isLocale());
648
ResourceBundle res = specToOpen.getBundle();
649
650         if (res == null) {
651             // This means that the bundle's locale does not match
652
// the current level of iteration for the spec.
653
return null;
654         }
655
656         for (int pass=0; pass<2; ++pass) {
657             StringBuffer JavaDoc tag = new StringBuffer JavaDoc();
658             // First try either TransliteratorTo_xxx or
659
// TransliterateFrom_xxx, then try the bidirectional
660
// Transliterate_xxx. This precedence order is arbitrary
661
// but must be consistent and documented.
662
if (pass == 0) {
663                 tag.append(direction == Transliterator.FORWARD ?
664                            "TransliterateTo" : "TransliterateFrom");
665             } else {
666                 tag.append("Transliterate");
667             }
668             tag.append(specToFind.get().toUpperCase());
669
670             try {
671                 // The Transliterate*_xxx resource is an array of
672
// strings of the format { <v0>, <r0>, ... }. Each
673
// <vi> is a variant name, and each <ri> is a rule.
674
String JavaDoc[] subres = res.getStringArray(tag.toString());
675
676                 // assert(subres != null);
677
// assert(subres.length % 2 == 0);
678
int i = 0;
679                 if (variant.length() != 0) {
680                     for (i=0; i<subres.length; i+= 2) {
681                         if (subres[i].equalsIgnoreCase(variant)) {
682                             break;
683                         }
684                     }
685                 }
686
687                 if (i < subres.length) {
688                     // We have a match, or there is no variant and i == 0.
689
// We have succeeded in loading a string from the
690
// locale resources. Return the rule string which
691
// will itself become the registry entry.
692

693                     // The direction is always forward for the
694
// TransliterateTo_xxx and TransliterateFrom_xxx
695
// items; those are unidirectional forward rules.
696
// For the bidirectional Transliterate_xxx items,
697
// the direction is the value passed in to this
698
// function.
699
int dir = (pass == 0) ? Transliterator.FORWARD : direction;
700                     return new Object JavaDoc[] { new LocaleEntry(subres[i+1], dir) };
701                 }
702
703             } catch (MissingResourceException JavaDoc e) {
704                 if (DEBUG) System.out.println("missing resource: " + e);
705             }
706         }
707
708         // If we get here we had a missing resource exception or we
709
// failed to find a desired variant.
710
return null;
711     }
712
713     /**
714      * Convenience method. Calls 3-arg find().
715      */

716     private Object JavaDoc[] find(String JavaDoc ID) {
717         String JavaDoc[] stv = TransliteratorIDParser.IDtoSTV(ID);
718         return find(stv[0], stv[1], stv[2]);
719     }
720
721     /**
722      * Top-level find method. Attempt to find a source-target/variant in
723      * either the dynamic or the static (locale resource) store. Perform
724      * fallback.
725      *
726      * Lookup sequence for ss_SS_SSS-tt_TT_TTT/v:
727      *
728      * ss_SS_SSS-tt_TT_TTT/v -- in hashtable
729      * ss_SS_SSS-tt_TT_TTT/v -- in ss_SS_SSS (no fallback)
730      *
731      * repeat with t = tt_TT_TTT, tt_TT, tt, and tscript
732      *
733      * ss_SS_SSS-t/*
734      * ss_SS-t/*
735      * ss-t/*
736      * sscript-t/*
737      *
738      * Here * matches the first variant listed.
739      *
740      * Caller does NOT own returned object. Return 0 on failure.
741      */

742     private Object JavaDoc[] find(String JavaDoc source,
743                           String JavaDoc target,
744                           String JavaDoc variant) {
745
746         Spec src = new Spec(source);
747         Spec trg = new Spec(target);
748         Object JavaDoc[] 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 JavaDoc ID,
806                                             Object JavaDoc[] entryWrapper,
807                                             StringBuffer JavaDoc aliasReturn) {
808         // We actually modify the entry object in some cases. If it
809
// is a string, we may partially parse it and turn it into a
810
// more processed precursor. This makes the next
811
// instantiation faster and allows sharing of immutable
812
// components like the RuleBasedTransliterator.Data objects.
813
// For this reason, the entry object is an Object[] of length
814
// 1.
815

816         for (;;) {
817             Object JavaDoc entry = entryWrapper[0];
818
819             if (entry instanceof RuleBasedTransliterator.Data) {
820                 RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) entry;
821                 return new RuleBasedTransliterator(ID, data, null);
822             } else if (entry instanceof Class JavaDoc) {
823                 try {
824                     return (Transliterator) ((Class JavaDoc) entry).newInstance();
825                 } catch (InstantiationException JavaDoc e) {
826                 } catch (IllegalAccessException JavaDoc e2) {}
827                 return null;
828             } else if (entry instanceof AliasEntry) {
829                 aliasReturn.append(((AliasEntry) entry).alias);
830                 return null;
831             } else if (entry instanceof Transliterator.Factory) {
832                 return ((Transliterator.Factory) entry).getInstance(ID);
833             } else if (entry instanceof CompoundRBTEntry) {
834                 return ((CompoundRBTEntry) entry).getInstance();
835             } else if (entry instanceof Transliterator) {
836                 return (Transliterator) entry;
837             }
838
839             // At this point entry type must be either RULES_FORWARD or
840
// RULES_REVERSE. We process the rule data into a
841
// TransliteratorRuleData object, and possibly also into an
842
// .id header and/or footer. Then we modify the registry with
843
// the parsed data and retry.
844

845             TransliteratorParser parser = new TransliteratorParser();
846
847             try {
848                
849                 ResourceEntry re = (ResourceEntry) entry;
850                 parser.parse(re.resource, re.direction);
851                 
852             } catch (ClassCastException JavaDoc e) {
853                 // If we pull a rule from a locale resource bundle it will
854
// be a LocaleEntry.
855
LocaleEntry le = (LocaleEntry) entry;
856                 parser.parse(le.rule, le.direction);
857             }
858
859             // Reset entry to something that we process at the
860
// top of the loop, then loop back to the top. As long as we
861
// do this, we only loop through twice at most.
862
// NOTE: The logic here matches that in
863
// Transliterator.createFromRules().
864
if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) {
865                 // No idBlock, no data -- this is just an
866
// alias for Null
867
entryWrapper[0] = new AliasEntry(NullTransliterator._ID);
868             }
869             else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) {
870                 // No idBlock, data != 0 -- this is an
871
// ordinary RBT_DATA
872
entryWrapper[0] = parser.dataVector.get(0);
873             }
874             else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) {
875                 // idBlock, no data -- this is an alias. The ID has
876
// been munged from reverse into forward mode, if
877
// necessary, so instantiate the ID in the forward
878
// direction.
879
if (parser.compoundFilter != null)
880                     entryWrapper[0] = new AliasEntry(parser.compoundFilter.toPattern(false) + ";"
881                             + (String JavaDoc)parser.idBlockVector.get(0));
882                 else
883                     entryWrapper[0] = new AliasEntry((String JavaDoc)parser.idBlockVector.get(0));
884             }
885             else {
886                 entryWrapper[0] = new CompoundRBTEntry(ID, parser.idBlockVector, parser.dataVector,
887                         parser.compoundFilter);
888             }
889         }
890     }
891 }
892
893 //eof
894
Popular Tags