blob: 1d5ac576f5ca919adf90cb5815da092e527e9093 [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 2001-2003, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/demo/translit/AnyTransliterator.java,v $
* $Date: 2003/06/03 18:49:27 $
* $Revision: 1.5 $
*
*******************************************************************************
*/
package com.ibm.icu.dev.demo.translit;
import com.ibm.icu.lang.*;
import com.ibm.icu.text.*;
import java.util.*;
public class AnyTransliterator extends Transliterator {
static final boolean DEBUG = false;
private String targetName;
private RunIterator it;
private Position run;
public AnyTransliterator(String targetName, UnicodeFilter filter, RunIterator it){
super("Any-" + targetName, filter);
this.targetName = targetName;
this.it = it;
run = new Position();
}
public AnyTransliterator(String targetName, UnicodeFilter filter){
this(targetName, filter, new ScriptRunIterator());
}
static private Transliterator hex = Transliterator.getInstance("[^\\u0020-\\u007E] hex");
protected void handleTransliterate(Replaceable text,
Position offsets, boolean isIncremental) {
if (DEBUG) {
System.out.println("- handleTransliterate " + hex.transliterate(text.toString())
+ ", " + toString(offsets));
}
it.reset(text, offsets);
while (it.next(run)) {
if (targetName.equalsIgnoreCase(it.getName())) {
if (DEBUG) System.out.println("Skipping identical: " + targetName);
run.start = run.limit; // show we processed
continue; // skip if same
}
Transliterator t;
String id = it.getName() + '-' + targetName;
try {
t = Transliterator.getInstance(id);
} catch (IllegalArgumentException ex) {
if (DEBUG) System.out.println("Couldn't find: " + id + ", Trying Latin as Pivot");
id = it.getName() + "-Latin; Latin-" + targetName;
try {
t = Transliterator.getInstance(id);
} catch (IllegalArgumentException ex2) {
if (DEBUG) System.out.println("Couldn't find: " + id);
continue;
}
}
// TODO catch error later!!
if (DEBUG) {
System.out.println(t.getID());
System.out.println("input: " + hex.transliterate(text.toString())
+ ", " + toString(run));
}
if (isIncremental && it.atEnd()) {
t.transliterate(text, run);
} else {
t.finishTransliteration(text, run);
}
// adjust the offsets in line with the changes
it.adjust(run.limit);
if (DEBUG) {
System.out.println("output: " + hex.transliterate(text.toString())
+ ", " + toString(run));
}
}
// show how far we got!
it.getExpanse(offsets);
if (run.start == run.limit) offsets.start = offsets.limit;
else offsets.start = run.start;
if (DEBUG) {
System.out.println("+ handleTransliterate: " + ", " + toString(offsets));
System.out.println();
}
}
// should be method on Position
public static String toString(Position offsets) {
return "[cs: " + offsets.contextStart
+ ", s: " + offsets.start
+ ", l: " + offsets.limit
+ ", cl: " + offsets.contextLimit
+ "]";
}
public interface RunIterator {
public void reset(Replaceable text, Position expanse);
public void getExpanse(Position run);
public void reset();
public boolean next(Position run);
public void getCurrent(Position run);
public String getName();
public void adjust(int newCurrentLimit);
public boolean atEnd();
}
/**
* Returns a series of ranges corresponding to scripts. They will be of the form:
* ccccSScSSccccTTcTcccc - where c is common, S is the first script and T is the second
*| | - first run
* | | - second run
* That is, the runs will overlap. The reason for this is so that a transliterator can
* consider common characters both before and after the scripts.
* The only time that contextStart != start is for the first run
* (the context is the start context of the entire expanse)
* The only time that contextLimit != limit is for the last run
* (the context is the end context of the entire expanse)
*/
public static class ScriptRunIterator implements RunIterator {
private Replaceable text;
private Position expanse = new Position();
private Position current = new Position();
private int script;
private boolean done = true;
public void reset(Replaceable text, Position expanse) {
set(this.expanse, expanse);
this.text = text;
reset();
}
public void reset() {
done = false;
//this.expanse = expanse;
script = UScript.INVALID_CODE;
// set up first range to be empty, at beginning
current.contextStart = expanse.contextStart;
current.start = current.limit = current.contextLimit = expanse.start;
}
public boolean next(Position run) {
if (done) return false;
if (DEBUG) {
System.out.println("+cs: " + current.contextStart
+ ", s: " + current.start
+ ", l: " + current.limit
+ ", cl: " + current.contextLimit);
}
// reset start context run to the last end
current.start = current.limit;
// Phase 1. Backup the START value through COMMON until we get to expanse.start or a real script.
int i, cp;
int limit = expanse.start;
for (i = current.start; i > limit; i -= UTF16.getCharCount(cp)) {
cp = text.char32At(i);
int script = UScript.getScript(cp);
if (script != UScript.COMMON && script != UScript.INHERITED) break;
}
current.start = i;
current.contextStart = (i == limit) ? expanse.contextStart : i; // extend at start
// PHASE 2. Move up the LIMIT value through COMMON or single script until we get to expanse.limit
int lastScript = UScript.COMMON;
//int veryLastScript = UScript.COMMON;
limit = expanse.limit;
for (i = current.limit; i < limit; i += UTF16.getCharCount(cp)) {
cp = text.char32At(i);
int script = UScript.getScript(cp);
if (script == UScript.INHERITED) script = UScript.COMMON;
if (script != UScript.COMMON) {
// if we find a real script:
// if we already had a script, bail
// otherwise set our script
if (lastScript == UScript.COMMON) lastScript = script;
else if (lastScript != script) break;
}
}
current.limit = i;
current.contextLimit = (i == limit) ? expanse.contextLimit : i; // extend at end
done = (i == limit);
script = lastScript;
if (DEBUG) {
System.out.println("-cs: " + current.contextStart
+ ", s: " + current.start
+ ", l: " + current.limit
+ ", cl: " + current.contextLimit);
}
set(run, current);
return true;
}
// SHOULD BE METHOD ON POSITION
public static void set(Position run, Position current) {
run.contextStart = current.contextStart;
run.start = current.start;
run.limit = current.limit;
run.contextLimit = current.contextLimit;
}
public boolean atEnd() {
return current.limit == expanse.limit;
}
public void getCurrent(Position run) {
set(run, current);
}
public void getExpanse(Position run) {
set(run, expanse);
}
public String getName() {
return UScript.getName(script);
}
public void adjust(int newCurrentLimit) {
if (expanse == null) {
throw new IllegalArgumentException("Must reset() before calling");
}
int delta = newCurrentLimit - current.limit;
current.limit += delta;
current.contextLimit += delta;
expanse.limit += delta;
expanse.contextLimit += delta;
}
// register Any-Script for every script.
private static Set scriptList = new HashSet();
public static void registerAnyToScript() {
synchronized (scriptList) {
Enumeration sources = Transliterator.getAvailableSources();
while(sources.hasMoreElements()) {
String source = (String) sources.nextElement();
if (source.equals("Any")) continue; // to keep from looping
Enumeration targets = Transliterator.getAvailableTargets(source);
while(targets.hasMoreElements()) {
String target = (String) targets.nextElement();
if (UScript.getCode(target) == null) continue; // SKIP unless we have a script (or locale)
if (scriptList.contains(target)) continue; // already encountered
scriptList.add(target); // otherwise add for later testing
Set variantSet = add(new TreeSet(), Transliterator.getAvailableVariants(source, target));
if (variantSet.size() < 2) {
AnyTransliterator at = new AnyTransliterator(target, null);
DummyFactory.add(at.getID(), at);
} else {
Iterator variants = variantSet.iterator();
while(variants.hasNext()) {
String variant = (String) variants.next();
AnyTransliterator at = new AnyTransliterator(
(variant.length() > 0) ? target + "/" + variant : target, null);
DummyFactory.add(at.getID(), at);
}
}
}
}
}
}
static class DummyFactory implements Transliterator.Factory {
static DummyFactory singleton = new DummyFactory();
static HashMap m = new HashMap();
// Since Transliterators are immutable, we don't have to clone on set & get
static void add(String ID, Transliterator t) {
m.put(ID, t);
System.out.println("Registering: " + ID + ", " + t.toRules(true));
Transliterator.registerFactory(ID, singleton);
}
public Transliterator getInstance(String ID) {
return (Transliterator) m.get(ID);
}
}
// Nice little Utility for converting Enumeration to collection
static Set add(Set s, Enumeration enum) {
while(enum.hasMoreElements()) {
s.add(enum.nextElement());
}
return s;
}
}
}