blob: 48333b5ec224fa01084cb986223049b334bfa0ae [file] [log] [blame]
/*
**********************************************************************
* Copyright (c) 2001-2011, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Date Name Description
* 08/19/2001 aliu Creation.
**********************************************************************
*/
package com.ibm.icu.text;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.lang.UScript;
import com.ibm.icu.text.RuleBasedTransliterator.Data;
import com.ibm.icu.util.CaseInsensitiveString;
import com.ibm.icu.util.UResourceBundle;
class TransliteratorRegistry {
// char constants
private static final char LOCALE_SEP = '_';
// String constants
private static final String NO_VARIANT = ""; // empty string
private static final String ANY = "Any";
/**
* Dynamic registry mapping full IDs to Entry objects. This
* contains both public and internal entities. The visibility is
* controlled by whether an entry is listed in availableIDs and
* specDAG or not.
*
* Keys are CaseInsensitiveString objects.
* Values are objects of class Class (subclass of Transliterator),
* RuleBasedTransliterator.Data, Transliterator.Factory, or one
* of the entry classes defined here (AliasEntry or ResourceEntry).
*/
private Map<CaseInsensitiveString, Object[]> registry;
/**
* DAG of visible IDs by spec. Hashtable: source => (Hashtable:
* target => (Vector: variant)) The Vector of variants is never
* empty. For a source-target with no variant, the special
* variant NO_VARIANT (the empty string) is stored in slot zero of
* the UVector.
*
* Keys are CaseInsensitiveString objects.
* Values are Hashtable of (CaseInsensitiveString -> Vector of
* CaseInsensitiveString)
*/
private Map<CaseInsensitiveString, Map<CaseInsensitiveString, List<CaseInsensitiveString>>> specDAG;
/**
* Vector of public full IDs (CaseInsensitiveString objects).
*/
private List<CaseInsensitiveString> availableIDs;
//----------------------------------------------------------------------
// class Spec
//----------------------------------------------------------------------
/**
* A Spec is a string specifying either a source or a target. In more
* general terms, it may also specify a variant, but we only use the
* Spec class for sources and targets.
*
* A Spec may be a locale or a script. If it is a locale, it has a
* fallback chain that goes xx_YY_ZZZ -> xx_YY -> xx -> ssss, where
* ssss is the script mapping of xx_YY_ZZZ. The Spec API methods
* hasFallback(), next(), and reset() iterate over this fallback
* sequence.
*
* The Spec class canonicalizes itself, so the locale is put into
* canonical form, or the script is transformed from an abbreviation
* to a full name.
*/
static class Spec {
private String top; // top spec
private String spec; // current spec
private String nextSpec; // next spec
private String scriptName; // script name equivalent of top, if != top
private boolean isSpecLocale; // TRUE if spec is a locale
private boolean isNextLocale; // TRUE if nextSpec is a locale
private ICUResourceBundle res;
public Spec(String theSpec) {
top = theSpec;
spec = null;
scriptName = null;
try{
// Canonicalize script name. If top is a script name then
// script != UScript.INVALID_CODE.
int script = UScript.getCodeFromName(top);
// Canonicalize script name -or- do locale->script mapping
int[] s = UScript.getCode(top);
if (s != null) {
scriptName = UScript.getName(s[0]);
// If the script name is the same as top then it's redundant
if (scriptName.equalsIgnoreCase(top)) {
scriptName = null;
}
}
isSpecLocale = false;
res = null;
// If 'top' is not a script name, try a locale lookup
if (script == UScript.INVALID_CODE) {
Locale toploc = LocaleUtility.getLocaleFromName(top);
res = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_TRANSLIT_BASE_NAME,toploc);
// Make sure we got the bundle we wanted; otherwise, don't use it
if (res!=null && LocaleUtility.isFallbackOf(res.getULocale().toString(), top)) {
isSpecLocale = true;
}
}
}catch(MissingResourceException e){
///CLOVER:OFF
// The constructor is called from multiple private methods
// that protects an invalid scriptName
scriptName = null;
///CLOVER:ON
}
// assert(spec != top);
reset();
}
public boolean hasFallback() {
return nextSpec != null;
}
public void reset() {
if (spec != top) { // [sic] pointer comparison
spec = top;
isSpecLocale = (res != null);
setupNext();
}
}
private void setupNext() {
isNextLocale = false;
if (isSpecLocale) {
nextSpec = spec;
int i = nextSpec.lastIndexOf(LOCALE_SEP);
// If i == 0 then we have _FOO, so we fall through
// to the scriptName.
if (i > 0) {
nextSpec = spec.substring(0, i);
isNextLocale = true;
} else {
nextSpec = scriptName; // scriptName may be null
}
} else {
// Fallback to the script, which may be null
if (nextSpec != scriptName) {
nextSpec = scriptName;
} else {
nextSpec = null;
}
}
}
// Protocol:
// for(String& s(spec.get());
// spec.hasFallback(); s(spec.next())) { ...
public String next() {
spec = nextSpec;
isSpecLocale = isNextLocale;
setupNext();
return spec;
}
public String get() {
return spec;
}
public boolean isLocale() {
return isSpecLocale;
}
/**
* Return the ResourceBundle for this spec, at the current
* level of iteration. The level of iteration goes from
* aa_BB_CCC to aa_BB to aa. If the bundle does not
* correspond to the current level of iteration, return null.
* If isLocale() is false, always return null.
*/
public ResourceBundle getBundle() {
if (res != null &&
res.getULocale().toString().equals(spec)) {
return res;
}
return null;
}
public String getTop() {
return top;
}
}
//----------------------------------------------------------------------
// Entry classes
//----------------------------------------------------------------------
static class ResourceEntry {
public String resource;
public String encoding;
public int direction;
public ResourceEntry(String n, String enc, int d) {
resource = n;
encoding = enc;
direction = d;
}
}
// An entry representing a rule in a locale resource bundle
static class LocaleEntry {
public String rule;
public int direction;
public LocaleEntry(String r, int d) {
rule = r;
direction = d;
}
}
static class AliasEntry {
public String alias;
public AliasEntry(String a) {
alias = a;
}
}
static class CompoundRBTEntry {
private String ID;
private List<String> idBlockVector;
private List<Data> dataVector;
private UnicodeSet compoundFilter;
public CompoundRBTEntry(String theID, List<String> theIDBlockVector,
List<Data> theDataVector,
UnicodeSet theCompoundFilter) {
ID = theID;
idBlockVector = theIDBlockVector;
dataVector = theDataVector;
compoundFilter = theCompoundFilter;
}
public Transliterator getInstance() {
List<Transliterator> transliterators = new ArrayList<Transliterator>();
int passNumber = 1;
int limit = Math.max(idBlockVector.size(), dataVector.size());
for (int i = 0; i < limit; i++) {
if (i < idBlockVector.size()) {
String idBlock = idBlockVector.get(i);
if (idBlock.length() > 0)
transliterators.add(Transliterator.getInstance(idBlock));
}
if (i < dataVector.size()) {
Data data = dataVector.get(i);
transliterators.add(new RuleBasedTransliterator("%Pass" + passNumber++, data, null));
}
}
Transliterator t = new CompoundTransliterator(transliterators, passNumber - 1);
t.setID(ID);
if (compoundFilter != null) {
t.setFilter(compoundFilter);
}
return t;
}
}
//----------------------------------------------------------------------
// class TransliteratorRegistry: Basic public API
//----------------------------------------------------------------------
public TransliteratorRegistry() {
registry = Collections.synchronizedMap(new HashMap<CaseInsensitiveString, Object[]>());
specDAG = Collections.synchronizedMap(new HashMap<CaseInsensitiveString, Map<CaseInsensitiveString, List<CaseInsensitiveString>>>());
availableIDs = new ArrayList<CaseInsensitiveString>();
}
/**
* Given a simple ID (forward direction, no inline filter, not
* compound) attempt to instantiate it from the registry. Return
* 0 on failure.
*
* Return a non-empty aliasReturn value if the ID points to an alias.
* We cannot instantiate it ourselves because the alias may contain
* filters or compounds, which we do not understand. Caller should
* make aliasReturn empty before calling.
*/
public Transliterator get(String ID,
StringBuffer aliasReturn) {
Object[] entry = find(ID);
return (entry == null) ? null
: instantiateEntry(ID, entry, aliasReturn);
}
/**
* Register a class. This adds an entry to the
* dynamic store, or replaces an existing entry. Any entry in the
* underlying static locale resource store is masked.
*/
public void put(String ID,
Class<? extends Transliterator> transliteratorSubclass,
boolean visible) {
registerEntry(ID, transliteratorSubclass, visible);
}
/**
* Register an ID and a factory function pointer. This adds an
* entry to the dynamic store, or replaces an existing entry. Any
* entry in the underlying static locale resource store is masked.
*/
public void put(String ID,
Transliterator.Factory factory,
boolean visible) {
registerEntry(ID, factory, visible);
}
/**
* Register an ID and a resource name. This adds an entry to the
* dynamic store, or replaces an existing entry. Any entry in the
* underlying static locale resource store is masked.
*/
public void put(String ID,
String resourceName,
String encoding,
int dir,
boolean visible) {
registerEntry(ID, new ResourceEntry(resourceName, encoding, dir), visible);
}
/**
* Register an ID and an alias ID. This adds an entry to the
* dynamic store, or replaces an existing entry. Any entry in the
* underlying static locale resource store is masked.
*/
public void put(String ID,
String alias,
boolean visible) {
registerEntry(ID, new AliasEntry(alias), visible);
}
/**
* Register an ID and a Transliterator object. This adds an entry
* to the dynamic store, or replaces an existing entry. Any entry
* in the underlying static locale resource store is masked.
*/
public void put(String ID,
Transliterator trans,
boolean visible) {
registerEntry(ID, trans, visible);
}
/**
* Unregister an ID. This removes an entry from the dynamic store
* if there is one. The static locale resource store is
* unaffected.
*/
public void remove(String ID) {
String[] stv = TransliteratorIDParser.IDtoSTV(ID);
// Only need to do this if ID.indexOf('-') < 0
String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
registry.remove(new CaseInsensitiveString(id));
removeSTV(stv[0], stv[1], stv[2]);
availableIDs.remove(new CaseInsensitiveString(id));
}
//----------------------------------------------------------------------
// class TransliteratorRegistry: Public ID and spec management
//----------------------------------------------------------------------
/**
* An internal class that adapts an enumeration over
* CaseInsensitiveStrings to an enumeration over Strings.
*/
private static class IDEnumeration implements Enumeration<String> {
Enumeration<CaseInsensitiveString> en;
public IDEnumeration(Enumeration<CaseInsensitiveString> e) {
en = e;
}
public boolean hasMoreElements() {
return en != null && en.hasMoreElements();
}
public String nextElement() {
return (en.nextElement()).getString();
}
}
/**
* Returns an enumeration over the programmatic names of visible
* registered transliterators.
*
* @return An <code>Enumeration</code> over <code>String</code> objects
*/
public Enumeration<String> getAvailableIDs() {
// Since the cache contains CaseInsensitiveString objects, but
// the caller expects Strings, we have to use an intermediary.
return new IDEnumeration(Collections.enumeration(availableIDs));
}
/**
* Returns an enumeration over all visible source names.
*
* @return An <code>Enumeration</code> over <code>String</code> objects
*/
public Enumeration<String> getAvailableSources() {
return new IDEnumeration(Collections.enumeration(specDAG.keySet()));
}
/**
* Returns an enumeration over visible target names for the given
* source.
*
* @return An <code>Enumeration</code> over <code>String</code> objects
*/
public Enumeration<String> getAvailableTargets(String source) {
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
Map<CaseInsensitiveString, List<CaseInsensitiveString>> targets = specDAG.get(cisrc);
if (targets == null) {
return new IDEnumeration(null);
}
return new IDEnumeration(Collections.enumeration(targets.keySet()));
}
/**
* Returns an enumeration over visible variant names for the given
* source and target.
*
* @return An <code>Enumeration</code> over <code>String</code> objects
*/
public Enumeration<String> getAvailableVariants(String source, String target) {
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
CaseInsensitiveString citrg = new CaseInsensitiveString(target);
Map<CaseInsensitiveString, List<CaseInsensitiveString>> targets = specDAG.get(cisrc);
if (targets == null) {
return new IDEnumeration(null);
}
List<CaseInsensitiveString> variants = targets.get(citrg);
if (variants == null) {
return new IDEnumeration(null);
}
return new IDEnumeration(Collections.enumeration(variants));
}
//----------------------------------------------------------------------
// class TransliteratorRegistry: internal
//----------------------------------------------------------------------
/**
* Convenience method. Calls 6-arg registerEntry().
*/
private void registerEntry(String source,
String target,
String variant,
Object entry,
boolean visible) {
String s = source;
if (s.length() == 0) {
s = ANY;
}
String ID = TransliteratorIDParser.STVtoID(source, target, variant);
registerEntry(ID, s, target, variant, entry, visible);
}
/**
* Convenience method. Calls 6-arg registerEntry().
*/
private void registerEntry(String ID,
Object entry,
boolean visible) {
String[] stv = TransliteratorIDParser.IDtoSTV(ID);
// Only need to do this if ID.indexOf('-') < 0
String id = TransliteratorIDParser.STVtoID(stv[0], stv[1], stv[2]);
registerEntry(id, stv[0], stv[1], stv[2], entry, visible);
}
/**
* Register an entry object (adopted) with the given ID, source,
* target, and variant strings.
*/
private void registerEntry(String ID,
String source,
String target,
String variant,
Object entry,
boolean visible) {
CaseInsensitiveString ciID = new CaseInsensitiveString(ID);
Object[] arrayOfObj;
// Store the entry within an array so it can be modified later
if (entry instanceof Object[]) {
arrayOfObj = (Object[])entry;
} else {
arrayOfObj = new Object[] { entry };
}
registry.put(ciID, arrayOfObj);
if (visible) {
registerSTV(source, target, variant);
if (!availableIDs.contains(ciID)) {
availableIDs.add(ciID);
}
} else {
removeSTV(source, target, variant);
availableIDs.remove(ciID);
}
}
/**
* Register a source-target/variant in the specDAG. Variant may be
* empty, but source and target must not be. If variant is empty then
* the special variant NO_VARIANT is stored in slot zero of the
* UVector of variants.
*/
private void registerSTV(String source,
String target,
String variant) {
// assert(source.length() > 0);
// assert(target.length() > 0);
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
CaseInsensitiveString citrg = new CaseInsensitiveString(target);
CaseInsensitiveString civar = new CaseInsensitiveString(variant);
Map<CaseInsensitiveString, List<CaseInsensitiveString>> targets = specDAG.get(cisrc);
if (targets == null) {
targets = Collections.synchronizedMap(new HashMap<CaseInsensitiveString, List<CaseInsensitiveString>>());
specDAG.put(cisrc, targets);
}
List<CaseInsensitiveString> variants = targets.get(citrg);
if (variants == null) {
variants = new ArrayList<CaseInsensitiveString>();
targets.put(citrg, variants);
}
// assert(NO_VARIANT == "");
// We add the variant string. If it is the special "no variant"
// string, that is, the empty string, we add it at position zero.
if (!variants.contains(civar)) {
if (variant.length() > 0) {
variants.add(civar);
} else {
variants.add(0, civar);
}
}
}
/**
* Remove a source-target/variant from the specDAG.
*/
private void removeSTV(String source,
String target,
String variant) {
// assert(source.length() > 0);
// assert(target.length() > 0);
CaseInsensitiveString cisrc = new CaseInsensitiveString(source);
CaseInsensitiveString citrg = new CaseInsensitiveString(target);
CaseInsensitiveString civar = new CaseInsensitiveString(variant);
Map<CaseInsensitiveString, List<CaseInsensitiveString>> targets = specDAG.get(cisrc);
if (targets == null) {
return; // should never happen for valid s-t/v
}
List<CaseInsensitiveString> variants = targets.get(citrg);
if (variants == null) {
return; // should never happen for valid s-t/v
}
variants.remove(civar);
if (variants.size() == 0) {
targets.remove(citrg); // should delete variants
if (targets.size() == 0) {
specDAG.remove(cisrc); // should delete targets
}
}
}
private static final boolean DEBUG = false;
/**
* Attempt to find a source-target/variant in the dynamic registry
* store. Return 0 on failure.
*/
private Object[] findInDynamicStore(Spec src,
Spec trg,
String variant) {
String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
///CLOVER:OFF
if (DEBUG) {
System.out.println("TransliteratorRegistry.findInDynamicStore:" +
ID);
}
///CLOVER:ON
return registry.get(new CaseInsensitiveString(ID));
}
/**
* Attempt to find a source-target/variant in the static locale
* resource store. Do not perform fallback. Return 0 on failure.
*
* On success, create a new entry object, register it in the dynamic
* store, and return a pointer to it, but do not make it public --
* just because someone requested something, we do not expand the
* available ID list (or spec DAG).
*/
private Object[] findInStaticStore(Spec src,
Spec trg,
String variant) {
///CLOVER:OFF
if (DEBUG) {
String ID = TransliteratorIDParser.STVtoID(src.get(), trg.get(), variant);
System.out.println("TransliteratorRegistry.findInStaticStore:" +
ID);
}
///CLOVER:ON
Object[] entry = null;
if (src.isLocale()) {
entry = findInBundle(src, trg, variant, Transliterator.FORWARD);
} else if (trg.isLocale()) {
entry = findInBundle(trg, src, variant, Transliterator.REVERSE);
}
// If we found an entry, store it in the Hashtable for next
// time.
if (entry != null) {
registerEntry(src.getTop(), trg.getTop(), variant, entry, false);
}
return entry;
}
/**
* Attempt to find an entry in a single resource bundle. This is
* a one-sided lookup. findInStaticStore() performs up to two such
* lookups, one for the source, and one for the target.
*
* Do not perform fallback. Return 0 on failure.
*
* On success, create a new Entry object, populate it, and return it.
* The caller owns the returned object.
*/
private Object[] findInBundle(Spec specToOpen,
Spec specToFind,
String variant,
int direction) {
// assert(specToOpen.isLocale());
ResourceBundle res = specToOpen.getBundle();
if (res == null) {
// This means that the bundle's locale does not match
// the current level of iteration for the spec.
return null;
}
for (int pass=0; pass<2; ++pass) {
StringBuilder tag = new StringBuilder();
// First try either TransliteratorTo_xxx or
// TransliterateFrom_xxx, then try the bidirectional
// Transliterate_xxx. This precedence order is arbitrary
// but must be consistent and documented.
if (pass == 0) {
tag.append(direction == Transliterator.FORWARD ?
"TransliterateTo" : "TransliterateFrom");
} else {
tag.append("Transliterate");
}
tag.append(specToFind.get().toUpperCase(Locale.ENGLISH));
try {
// The Transliterate*_xxx resource is an array of
// strings of the format { <v0>, <r0>, ... }. Each
// <vi> is a variant name, and each <ri> is a rule.
String[] subres = res.getStringArray(tag.toString());
// assert(subres != null);
// assert(subres.length % 2 == 0);
int i = 0;
if (variant.length() != 0) {
for (i=0; i<subres.length; i+= 2) {
if (subres[i].equalsIgnoreCase(variant)) {
break;
}
}
}
if (i < subres.length) {
// We have a match, or there is no variant and i == 0.
// We have succeeded in loading a string from the
// locale resources. Return the rule string which
// will itself become the registry entry.
// The direction is always forward for the
// TransliterateTo_xxx and TransliterateFrom_xxx
// items; those are unidirectional forward rules.
// For the bidirectional Transliterate_xxx items,
// the direction is the value passed in to this
// function.
int dir = (pass == 0) ? Transliterator.FORWARD : direction;
return new Object[] { new LocaleEntry(subres[i+1], dir) };
}
} catch (MissingResourceException e) {
///CLOVER:OFF
if (DEBUG) System.out.println("missing resource: " + e);
///CLOVER:ON
}
}
// If we get here we had a missing resource exception or we
// failed to find a desired variant.
return null;
}
/**
* Convenience method. Calls 3-arg find().
*/
private Object[] find(String ID) {
String[] stv = TransliteratorIDParser.IDtoSTV(ID);
return find(stv[0], stv[1], stv[2]);
}
/**
* Top-level find method. Attempt to find a source-target/variant in
* either the dynamic or the static (locale resource) store. Perform
* fallback.
*
* Lookup sequence for ss_SS_SSS-tt_TT_TTT/v:
*
* ss_SS_SSS-tt_TT_TTT/v -- in hashtable
* ss_SS_SSS-tt_TT_TTT/v -- in ss_SS_SSS (no fallback)
*
* repeat with t = tt_TT_TTT, tt_TT, tt, and tscript
*
* ss_SS_SSS-t/*
* ss_SS-t/*
* ss-t/*
* sscript-t/*
*
* Here * matches the first variant listed.
*
* Caller does NOT own returned object. Return 0 on failure.
*/
private Object[] find(String source,
String target,
String variant) {
Spec src = new Spec(source);
Spec trg = new Spec(target);
Object[] entry = null;
if (variant.length() != 0) {
// Seek exact match in hashtable
entry = findInDynamicStore(src, trg, variant);
if (entry != null) {
return entry;
}
// Seek exact match in locale resources
entry = findInStaticStore(src, trg, variant);
if (entry != null) {
return entry;
}
}
for (;;) {
src.reset();
for (;;) {
// Seek match in hashtable
entry = findInDynamicStore(src, trg, NO_VARIANT);
if (entry != null) {
return entry;
}
// Seek match in locale resources
entry = findInStaticStore(src, trg, NO_VARIANT);
if (entry != null) {
return entry;
}
if (!src.hasFallback()) {
break;
}
src.next();
}
if (!trg.hasFallback()) {
break;
}
trg.next();
}
return null;
}
/**
* Given an Entry object, instantiate it. Caller owns result. Return
* 0 on failure.
*
* Return a non-empty aliasReturn value if the ID points to an alias.
* We cannot instantiate it ourselves because the alias may contain
* filters or compounds, which we do not understand. Caller should
* make aliasReturn empty before calling.
*
* The entry object is assumed to reside in the dynamic store. It may be
* modified.
*/
@SuppressWarnings("rawtypes")
private Transliterator instantiateEntry(String ID,
Object[] entryWrapper,
StringBuffer aliasReturn) {
// We actually modify the entry object in some cases. If it
// is a string, we may partially parse it and turn it into a
// more processed precursor. This makes the next
// instantiation faster and allows sharing of immutable
// components like the RuleBasedTransliterator.Data objects.
// For this reason, the entry object is an Object[] of length
// 1.
for (;;) {
Object entry = entryWrapper[0];
if (entry instanceof RuleBasedTransliterator.Data) {
RuleBasedTransliterator.Data data = (RuleBasedTransliterator.Data) entry;
return new RuleBasedTransliterator(ID, data, null);
} else if (entry instanceof Class) {
try {
return (Transliterator) ((Class) entry).newInstance();
} catch (InstantiationException e) {
} catch (IllegalAccessException e2) {}
return null;
} else if (entry instanceof AliasEntry) {
aliasReturn.append(((AliasEntry) entry).alias);
return null;
} else if (entry instanceof Transliterator.Factory) {
return ((Transliterator.Factory) entry).getInstance(ID);
} else if (entry instanceof CompoundRBTEntry) {
return ((CompoundRBTEntry) entry).getInstance();
} else if (entry instanceof AnyTransliterator) {
AnyTransliterator temp = (AnyTransliterator) entry;
return temp.safeClone();
} else if (entry instanceof RuleBasedTransliterator) {
RuleBasedTransliterator temp = (RuleBasedTransliterator) entry;
return temp.safeClone();
} else if (entry instanceof CompoundTransliterator) {
CompoundTransliterator temp = (CompoundTransliterator) entry;
return temp.safeClone();
} else if (entry instanceof Transliterator) {
return (Transliterator) entry;
}
// At this point entry type must be either RULES_FORWARD or
// RULES_REVERSE. We process the rule data into a
// TransliteratorRuleData object, and possibly also into an
// .id header and/or footer. Then we modify the registry with
// the parsed data and retry.
TransliteratorParser parser = new TransliteratorParser();
try {
ResourceEntry re = (ResourceEntry) entry;
parser.parse(re.resource, re.direction);
} catch (ClassCastException e) {
// If we pull a rule from a locale resource bundle it will
// be a LocaleEntry.
LocaleEntry le = (LocaleEntry) entry;
parser.parse(le.rule, le.direction);
}
// Reset entry to something that we process at the
// top of the loop, then loop back to the top. As long as we
// do this, we only loop through twice at most.
// NOTE: The logic here matches that in
// Transliterator.createFromRules().
if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 0) {
// No idBlock, no data -- this is just an
// alias for Null
entryWrapper[0] = new AliasEntry(NullTransliterator._ID);
}
else if (parser.idBlockVector.size() == 0 && parser.dataVector.size() == 1) {
// No idBlock, data != 0 -- this is an
// ordinary RBT_DATA
entryWrapper[0] = parser.dataVector.get(0);
}
else if (parser.idBlockVector.size() == 1 && parser.dataVector.size() == 0) {
// idBlock, no data -- this is an alias. The ID has
// been munged from reverse into forward mode, if
// necessary, so instantiate the ID in the forward
// direction.
if (parser.compoundFilter != null) {
entryWrapper[0] = new AliasEntry(parser.compoundFilter.toPattern(false) + ";"
+ parser.idBlockVector.get(0));
} else {
entryWrapper[0] = new AliasEntry(parser.idBlockVector.get(0));
}
}
else {
entryWrapper[0] = new CompoundRBTEntry(ID, parser.idBlockVector, parser.dataVector,
parser.compoundFilter);
}
}
}
}
//eof