Home | History | Annotate | Download | only in translit
      1 /*
      2  *******************************************************************************
      3  * Copyright (C) 1996-2012, International Business Machines Corporation and    *
      4  * others. All Rights Reserved.                                                *
      5  *******************************************************************************
      6  */
      7 package com.ibm.icu.dev.demo.translit;
      8 
      9 import java.awt.Button;
     10 import java.awt.CheckboxMenuItem;
     11 import java.awt.FileDialog;
     12 import java.awt.Font;
     13 import java.awt.Frame;
     14 import java.awt.GraphicsEnvironment;
     15 import java.awt.Label;
     16 import java.awt.Menu;
     17 import java.awt.MenuBar;
     18 import java.awt.MenuItem;
     19 import java.awt.MenuShortcut;
     20 import java.awt.TextField;
     21 import java.awt.event.ActionEvent;
     22 import java.awt.event.ActionListener;
     23 import java.awt.event.ItemEvent;
     24 import java.awt.event.ItemListener;
     25 import java.awt.event.KeyEvent;
     26 import java.awt.event.WindowAdapter;
     27 import java.awt.event.WindowEvent;
     28 import java.io.BufferedReader;
     29 import java.io.BufferedWriter;
     30 import java.io.File;
     31 import java.io.FileInputStream;
     32 import java.io.FileOutputStream;
     33 import java.io.InputStreamReader;
     34 import java.io.OutputStreamWriter;
     35 import java.io.PrintWriter;
     36 import java.text.CharacterIterator;
     37 import java.util.Comparator;
     38 import java.util.Enumeration;
     39 import java.util.HashMap;
     40 import java.util.Iterator;
     41 import java.util.Map;
     42 import java.util.Set;
     43 import java.util.TreeSet;
     44 
     45 import com.ibm.icu.impl.Differ;
     46 import com.ibm.icu.lang.UCharacter;
     47 import com.ibm.icu.text.BreakIterator;
     48 import com.ibm.icu.text.CanonicalIterator;
     49 import com.ibm.icu.text.Normalizer;
     50 import com.ibm.icu.text.ReplaceableString;
     51 import com.ibm.icu.text.Transliterator;
     52 import com.ibm.icu.text.UTF16;
     53 import com.ibm.icu.text.UnicodeSet;
     54 import com.ibm.icu.text.UnicodeSetIterator;
     55 
     56 /**
     57  * A frame that allows the user to experiment with keyboard
     58  * transliteration.  This class has a main() method so it can be run
     59  * as an application.  The frame contains an editable text component
     60  * and uses keyboard transliteration to process keyboard events.
     61  *
     62  * <p>Copyright (c) IBM Corporation 1999.  All rights reserved.
     63  *
     64  * @author Alan Liu
     65  */
     66 public class Demo extends Frame {
     67 
     68     /**
     69      * For serialization
     70      */
     71     private static final long serialVersionUID = 1L;
     72     static final boolean DEBUG = false;
     73     static final String START_TEXT = "(cut,\u03BA\u03C5\u03C4,\u05D0,\u30AF\u30C8,\u4E80,\u091A\u0941\u0924\u094D)";
     74 
     75     Transliterator translit = null;
     76     String fontName = "Arial Unicode MS";
     77     int fontSize = 18;
     78 
     79 
     80 
     81     /*
     82     boolean compound = false;
     83     Transliterator[] compoundTranslit = new Transliterator[MAX_COMPOUND];
     84     static final int MAX_COMPOUND = 128;
     85     int compoundCount = 0;
     86     */
     87 
     88     TransliteratingTextComponent text = null;
     89 
     90     Menu translitMenu;
     91     CheckboxMenuItem translitItem;
     92     CheckboxMenuItem noTranslitItem;
     93 
     94     static final String NO_TRANSLITERATOR = "None";
     95 
     96     //private static final String COPYRIGHT =
     97     //    "\u00A9 IBM Corporation 1999. All rights reserved.";
     98 
     99     public static void main(String[] args) {
    100         Frame f = new Demo(600, 200);
    101         f.addWindowListener(new WindowAdapter() {
    102             public void windowClosing(WindowEvent e) {
    103                 com.ibm.icu.dev.demo.impl.DemoApplet.demoFrameClosed();
    104 //                System.exit(0);
    105             }
    106         });
    107         f.setVisible(true);
    108         com.ibm.icu.dev.demo.impl.DemoApplet.demoFrameOpened();
    109     }
    110 
    111     public Demo(int width, int height) {
    112         super("Transliteration Demo");
    113 
    114         initMenus();
    115 
    116         addWindowListener(new WindowAdapter() {
    117             public void windowClosing(WindowEvent e) {
    118                 handleClose();
    119             }
    120         });
    121 
    122         text = new TransliteratingTextComponent();
    123         Font font = new Font(fontName, Font.PLAIN, fontSize);
    124         text.setFont(font);
    125         text.setSize(width, height);
    126         text.setVisible(true);
    127         text.setText(START_TEXT);
    128         add(text);
    129 
    130         setSize(width, height);
    131         setTransliterator("Latin-Greek", null);
    132     }
    133 
    134     private void initMenus() {
    135         MenuBar mbar;
    136         Menu menu;
    137         MenuItem mitem;
    138         //CheckboxMenuItem citem;
    139 
    140         setMenuBar(mbar = new MenuBar());
    141         mbar.add(menu = new Menu("File"));
    142         menu.add(mitem = new MenuItem("Quit"));
    143         mitem.addActionListener(new ActionListener() {
    144             public void actionPerformed(ActionEvent e) {
    145                 handleClose();
    146             }
    147         });
    148 /*
    149         final ItemListener setTransliteratorListener = new ItemListener() {
    150             public void itemStateChanged(ItemEvent e) {
    151                 CheckboxMenuItem item = (CheckboxMenuItem) e.getSource();
    152                 if (e.getStateChange() == ItemEvent.DESELECTED) {
    153                     // Don't let the current transliterator be deselected.
    154                     // Just reselect it.
    155                     item.setState(true);
    156                 } else if (compound) {
    157                     // Adding an item to a compound transliterator
    158                     handleAddToCompound(item.getLabel());
    159                 } else if (item != translitItem) {
    160                     // Deselect previous choice.  Don't need to call
    161                     // setState(true) on new choice.
    162                     translitItem.setState(false);
    163                     translitItem = item;
    164                     handleSetTransliterator(item.getLabel());
    165                 }
    166             }
    167         };
    168 */
    169         /*
    170         translitMenu.add(translitItem = noTranslitItem =
    171                          new CheckboxMenuItem(NO_TRANSLITERATOR, true));
    172         noTranslitItem.addItemListener(new ItemListener() {
    173             public void itemStateChanged(ItemEvent e) {
    174                 // Can't uncheck None -- any action here sets None to true
    175                 setNoTransliterator();
    176             }
    177         });
    178 
    179         translitMenu.addSeparator();
    180         */
    181 
    182 /*
    183         translitMenu.add(citem = new CheckboxMenuItem("Compound"));
    184         citem.addItemListener(new ItemListener() {
    185             public void itemStateChanged(ItemEvent e) {
    186                 CheckboxMenuItem item = (CheckboxMenuItem) e.getSource();
    187                 if (e.getStateChange() == ItemEvent.DESELECTED) {
    188                     // If compound gets deselected, then select NONE
    189                     setNoTransliterator();
    190                 } else if (!compound) {
    191                     // Switching from non-compound to compound
    192                     translitItem.setState(false);
    193                     translitItem = item;
    194                     translit = null;
    195                     compound = true;
    196                     compoundCount = 0;
    197                     for (int i=0; i<MAX_COMPOUND; ++i) {
    198                         compoundTranslit[i] = null;
    199                     }
    200                 }
    201             }
    202         });
    203 
    204         translitMenu.addSeparator();
    205        */
    206 
    207         /*
    208         for (Enumeration e=getSystemTransliteratorNames().elements();
    209              e.hasMoreElements(); ) {
    210             String s = (String) e.nextElement();
    211             translitMenu.add(citem = new CheckboxMenuItem(s));
    212             citem.addItemListener(setTransliteratorListener);
    213         }
    214         */
    215 
    216         Menu fontMenu = new Menu("Font");
    217         String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    218         for (int i = 0; i < fonts.length; ++i) {
    219             MenuItem mItem = new MenuItem(fonts[i]);
    220             mItem.addActionListener(new FontActionListener(fonts[i]));
    221             fontMenu.add(mItem);
    222         }
    223         mbar.add(fontMenu);
    224 
    225         Menu sizeMenu = new Menu("Size");
    226         int[] sizes = {9, 10, 12, 14, 18, 24, 36, 48, 72};
    227         for (int i = 0; i < sizes.length; ++i) {
    228             MenuItem mItem = new MenuItem("" + sizes[i]);
    229             mItem.addActionListener(new SizeActionListener(sizes[i]));
    230             sizeMenu.add(mItem);
    231         }
    232         mbar.add(sizeMenu);
    233 
    234         translit = null;
    235 
    236         mbar.add(translitMenu = new Menu("Transliterator"));
    237 
    238         translitMenu.add(convertSelectionItem = new MenuItem("Transliterate",
    239             new MenuShortcut(KeyEvent.VK_K)));
    240         convertSelectionItem.addActionListener(new ActionListener() {
    241             public void actionPerformed(ActionEvent e) {
    242                 handleBatchTransliterate(translit);
    243             }
    244         });
    245 
    246         translitMenu.add(swapSelectionItem = new MenuItem("Reverse",
    247             new MenuShortcut(KeyEvent.VK_S)));
    248         swapSelectionItem.addActionListener(new ActionListener() {
    249             public void actionPerformed(ActionEvent e) {
    250                 Transliterator inv;
    251                 try {
    252                     inv = translit.getInverse();
    253                 } catch (Exception x) {
    254                     inv = Transliterator.getInstance("null");
    255                 }
    256                 setTransliterator(inv.getID(), null);
    257             }
    258         });
    259 
    260         translitMenu.add(convertTypingItem = new MenuItem("No Typing Conversion",
    261             new MenuShortcut(KeyEvent.VK_T)));
    262         convertTypingItem.addActionListener(new ActionListener() {
    263             public void actionPerformed(ActionEvent e) {
    264                 if (!transliterateTyping) {
    265                     text.setTransliterator(translit);
    266                     convertTypingItem.setLabel("No Typing Conversion");
    267                 } else {
    268                     text.flush();
    269                     text.setTransliterator(null);
    270                     convertTypingItem.setLabel("Convert Typing");
    271                 }
    272                 transliterateTyping = !transliterateTyping;
    273             }
    274         });
    275 
    276         translitMenu.add(historyMenu = new Menu("Recent"));
    277 
    278         helpDialog = new InfoDialog(this, "Simple Demo", "Instructions",
    279            "CTL A, X, C, V have customary meanings.\n"
    280          + "Arrow keys, delete and backspace work.\n"
    281          + "To get a character from its control point, type the hex, then hit CTL Q"
    282         );
    283         helpDialog.getArea().setEditable(false);
    284 
    285 
    286         Menu helpMenu;
    287         mbar.add(helpMenu = new Menu("Extras"));
    288         helpMenu.add(mitem = new MenuItem("Help"));
    289         mitem.addActionListener(new ActionListener() {
    290             public void actionPerformed(ActionEvent e) {
    291                 helpDialog.show();
    292             }
    293         });
    294 
    295         hexDialog = new InfoDialog(this, "Hex Entry", "Use U+..., \\u..., \\x{...}, or &#x...;",
    296            "\\u00E1"
    297         );
    298         Button button = new Button("Insert");
    299         button.addActionListener(new ActionListener() {
    300             public void actionPerformed(ActionEvent e) {
    301                 String hexValue = hexDialog.getArea().getText();
    302                 text.insertText(fromHex.transliterate(hexValue));
    303             }
    304         });
    305         hexDialog.getBottom().add(button);
    306 
    307         helpMenu.add(mitem = new MenuItem("Hex...",
    308             new MenuShortcut(KeyEvent.VK_H)));
    309         mitem.addActionListener(new ActionListener() {
    310             public void actionPerformed(ActionEvent e) {
    311                 hexDialog.show();
    312             }
    313         });
    314 
    315         // Compound Transliterator
    316 
    317         compoundDialog = new InfoDialog(this, "Compound Transliterator", "",
    318            "[^\\u0000-\\u00FF] hex"
    319         );
    320         button = new Button("Set");
    321         button.addActionListener(new ActionListener() {
    322             public void actionPerformed(ActionEvent e) {
    323                 String compound = "";
    324                 try {
    325                     compound = compoundDialog.getArea().getText();
    326                     setTransliterator(compound, null);
    327                 } catch (RuntimeException ex) {
    328                     compoundDialog.getArea().setText(compound + "\n" + ex.getMessage());
    329                 }
    330             }
    331         });
    332         compoundDialog.getBottom().add(button);
    333 
    334         translitMenu.add(mitem = new MenuItem("Multiple...",
    335             new MenuShortcut(KeyEvent.VK_M)));
    336         mitem.addActionListener(new ActionListener() {
    337             public void actionPerformed(ActionEvent e) {
    338                 compoundDialog.show();
    339             }
    340         });
    341 
    342         // RuleBased Transliterator
    343 
    344         rulesDialog = new InfoDialog(this, "Rule-Based Transliterator", "",
    345            "([A-Z]) > &Hex($1) &Name($1);\r\n"
    346             + "&Hex-Any($1) < ('\\' [uU] [a-fA-F0-9]*);\r\n"
    347             + "&Name-Any($1) < ('{' [^\\}]* '}');"
    348         );
    349         button = new Button("Set");
    350         button.addActionListener(new ActionListener() {
    351             public void actionPerformed(ActionEvent e) {
    352                 String compound = "";
    353                 try {
    354                     compound = rulesDialog.getArea().getText();
    355                     String id = ruleId.getText();
    356                     setTransliterator(compound, id);
    357                 } catch (RuntimeException ex) {
    358                     rulesDialog.getArea().setText(compound + "\n#" + ex.getMessage());
    359                 }
    360             }
    361         });
    362         rulesDialog.getBottom().add(button);
    363         ruleId = new TextField("test1", 20);
    364         Label temp = new Label(" Name:");
    365         rulesDialog.getBottom().add(temp);
    366         rulesDialog.getBottom().add(ruleId);
    367 
    368 
    369         translitMenu.add(mitem = new MenuItem("From Rules...",
    370             new MenuShortcut(KeyEvent.VK_R)));
    371         mitem.addActionListener(new ActionListener() {
    372             public void actionPerformed(ActionEvent e) {
    373                 rulesDialog.show();
    374             }
    375         });
    376 
    377 
    378         translitMenu.add(mitem = new MenuItem("From File...",
    379             new MenuShortcut(KeyEvent.VK_F)));
    380         mitem.addActionListener(new FileListener(this, RULE_FILE));
    381 
    382         translitMenu.add(mitem = new MenuItem("Test File..."));
    383         mitem.addActionListener(new FileListener(this, TEST_FILE));
    384 
    385         // Flesh out the menu with the installed transliterators
    386 
    387         translitMenu.addSeparator();
    388 
    389         Iterator sources = add(new TreeSet(), Transliterator.getAvailableSources()).iterator();
    390         while(sources.hasNext()) {
    391             String source = (String) sources.next();
    392             Iterator targets = add(new TreeSet(), Transliterator.getAvailableTargets(source)).iterator();
    393             Menu targetMenu = new Menu(source);
    394             while(targets.hasNext()) {
    395                 String target = (String) targets.next();
    396                 Set variantSet = add(new TreeSet(), Transliterator.getAvailableVariants(source, target));
    397                 if (variantSet.size() < 2) {
    398                     mitem = new MenuItem(target);
    399                     mitem.addActionListener(new TransliterationListener(source + "-" + target));
    400                     targetMenu.add(mitem);
    401                 } else {
    402                     Iterator variants = variantSet.iterator();
    403                     Menu variantMenu = new Menu(target);
    404                     while(variants.hasNext()) {
    405                         String variant = (String) variants.next();
    406                         String menuName = variant.length() == 0 ? "<default>" : variant;
    407                         //System.out.println("<" + source + "-" + target + "/" + variant + ">, <" + menuName + ">");
    408                         mitem = new MenuItem(menuName);
    409                         mitem.addActionListener(new TransliterationListener(source + "-" + target + "/" + variant));
    410                         variantMenu.add(mitem);
    411                     }
    412                     targetMenu.add(variantMenu);
    413                 }
    414             }
    415             translitMenu.add(targetMenu);
    416         }
    417 
    418 
    419     }
    420 
    421     static final int RULE_FILE = 0, TEST_FILE = 1;
    422     //
    423     static class FileListener implements ActionListener {
    424         Demo frame;
    425         int choice;
    426 
    427         FileListener(Demo frame, int choice) {
    428             this.frame = frame;
    429             this.choice = choice;
    430         }
    431 
    432         public void actionPerformed(ActionEvent e) {
    433             String id = frame.translit.getID();
    434             int slashPos = id.indexOf('/');
    435             String variant = "";
    436             if (slashPos >= 0) {
    437                 variant = "_" + id.substring(slashPos+1);
    438                 id = id.substring(0, slashPos);
    439             }
    440 
    441             FileDialog fileDialog = new FileDialog(frame, "Input File");
    442             fileDialog.setFile("Test_" + id + ".txt");
    443             fileDialog.show();
    444             String fileName = fileDialog.getFile();
    445             String fileDirectory = fileDialog.getDirectory();
    446             if (fileName != null) {
    447                 try {
    448                     File f = new File(fileDirectory, fileName);
    449                     if (choice == RULE_FILE) {
    450 
    451                         // read stuff into buffer
    452 
    453                         StringBuffer buffer = new StringBuffer();
    454                         FileInputStream fis = new FileInputStream(f);
    455                         InputStreamReader isr = new InputStreamReader(fis, "UTF8");
    456                         BufferedReader br = new BufferedReader(isr, 32*1024);
    457                         while (true) {
    458                             String line = br.readLine();
    459                             if (line == null) break;
    460                             if (line.length() > 0 && line.charAt(0) == '\uFEFF') line = line.substring(1); // strip BOM
    461                             buffer.append('\n');
    462                             buffer.append(line);
    463                         }
    464                         br.close();
    465 
    466                         // Transform file name into id
    467                         if (fileName.startsWith("Transliterator_")) {
    468                             fileName = fileName.substring("Transliterator_".length());
    469                         }
    470                         int pos = fileName.indexOf('_');
    471                         if (pos < 0) {
    472                             id = fileName;
    473                         } else {
    474                             id = fileName.substring(0, pos) + "-";
    475                             int pos2 = fileName.indexOf('_', pos+1);
    476                             if (pos2 < 0) {
    477                                 id += fileName.substring(pos+1);
    478                             } else {
    479                                 id += fileName.substring(pos+1, pos2) + "/" + fileName.substring(pos2 + 1);
    480                             }
    481                         }
    482                         pos = id.lastIndexOf('.');
    483                         if (pos >= 0) id = id.substring(0, pos);
    484 
    485                         // Now set
    486 
    487                         frame.setTransliterator(buffer.toString(), id);
    488                     } else if (choice == TEST_FILE) {
    489                         genTestFile(f, frame.translit, variant);
    490                     }
    491                 } catch (Exception e2) {
    492                     e2.printStackTrace();
    493                     System.out.println("Problem opening/reading: " + fileDirectory + ", " + fileName);
    494                 }
    495             }
    496             fileDialog.dispose();
    497         }
    498     }
    499 
    500 
    501     boolean transliterateTyping = true;
    502     Transliterator fromHex = Transliterator.getInstance("Hex-Any");
    503     InfoDialog helpDialog;
    504     InfoDialog hexDialog;
    505     InfoDialog compoundDialog;
    506     InfoDialog rulesDialog;
    507     TextField ruleId;
    508     MenuItem convertSelectionItem = null;
    509     MenuItem swapSelectionItem = null;
    510     MenuItem convertTypingItem = null;
    511     Menu historyMenu;
    512     Map historyMap = new HashMap();
    513     Set historySet = new TreeSet(new Comparator() {
    514             public int compare(Object a, Object b) {
    515                 MenuItem aa = (MenuItem)a;
    516                 MenuItem bb = (MenuItem)b;
    517                 return aa.getLabel().compareTo(bb.getLabel());
    518             }
    519         });
    520 
    521     // ADD Factory since otherwise getInverse blows out
    522     static class DummyFactory implements Transliterator.Factory {
    523         static DummyFactory singleton = new DummyFactory();
    524         static HashMap m = new HashMap();
    525 
    526         // Since Transliterators are immutable, we don't have to clone on set & get
    527         static void add(String ID, Transliterator t) {
    528             m.put(ID, t);
    529             System.out.println("Registering: " + ID + ", " + t.toRules(true));
    530             Transliterator.registerFactory(ID, singleton);
    531         }
    532         public Transliterator getInstance(String ID) {
    533             return (Transliterator) m.get(ID);
    534         }
    535     }
    536 
    537     static void printBreaks(int num, String testSource, BreakIterator brkItr) {
    538         String result = "";
    539         int lastPos = 0;
    540         while (true) {
    541             int pos = brkItr.next();
    542             if (pos == BreakIterator.DONE) break;
    543             result += testSource.substring(lastPos, pos) + "&";
    544             lastPos = pos;
    545             System.out.println(pos);
    546         }
    547         System.out.println("Test" + num + ": " + result);
    548     }
    549 
    550     static void printIteration(int num, String testSource, CharacterIterator ci) {
    551         String result = "";
    552         while (true) {
    553             char ch = ci.next();
    554             if (ch == CharacterIterator.DONE) break;
    555             result += ch + "(" + ci.getIndex() + ")";
    556         }
    557         System.out.println("Test" + num + ": " + result);
    558     }
    559 
    560     static void printSources() {
    561         String[] list = {"Latin-ThaiLogical", "ThaiLogical-Latin", "Thai-ThaiLogical", "ThaiLogical-Thai"};
    562         UnicodeSet all = new UnicodeSet();
    563         for (int i = 0; i < list.length; ++i) {
    564             Transliterator tr = Transliterator.getInstance(list[i]);
    565             UnicodeSet src = tr.getSourceSet();
    566             System.out.println(list[i] + ": " + src.toPattern(true));
    567             all.addAll(src);
    568         }
    569         System.out.println("All: " + all.toPattern(true));
    570         UnicodeSet rem = new UnicodeSet("[[:latin:][:thai:]]");
    571         System.out.println("missing from [:latin:][:thai:]: " + all.removeAll(rem).toPattern(true));
    572     }
    573 
    574     // 200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;;
    575 
    576     static Transliterator title = Transliterator.getInstance("title");
    577     static String hexAndNameRules = "    ([:c:]) > \\u200E &hex/unicode($1) ' ( ) ' &name($1) \\u200E ' ';"
    578         + "([:mark:]) > \\u200E &hex/unicode($1) ' ( ' \\u200E \u25CC $1 \\u200E ' ) ' &name($1) \\u200E ' ';"
    579         + "(.) > \\u200E &hex/unicode($1) ' ( ' \\u200E $1 \\u200E ' ) ' &name($1) ' ' \\u200E;";
    580 
    581     static Transliterator hexAndName = Transliterator.createFromRules("any-hexAndName",
    582         hexAndNameRules, Transliterator.FORWARD);
    583 
    584 
    585 
    586     //static Transliterator upper = Transliterator.getInstance("upper");
    587 
    588     static final byte NONE = 0, TITLEWORD = 1, TITLELINE = 2;
    589 
    590     static void genTestFile(File sourceFile, Transliterator translit, String variant) {
    591         BufferedReader in = null;
    592         try {
    593 
    594             System.out.println("Reading: " + sourceFile.getCanonicalPath());
    595             in = new BufferedReader(
    596                 new InputStreamReader(
    597                     new FileInputStream(sourceFile), "UTF-8"));
    598             String targetFile = sourceFile.getCanonicalPath();
    599             int dotPos = targetFile.lastIndexOf('.');
    600             if (dotPos >= 0) targetFile = targetFile.substring(0,dotPos);
    601             targetFile += variant;
    602 
    603             File outFile = new File(targetFile + ".html");
    604             System.out.println("Writing: " + outFile.getCanonicalPath());
    605 
    606             PrintWriter out = new PrintWriter(
    607                 new BufferedWriter(
    608                     new OutputStreamWriter(
    609                         new FileOutputStream(outFile), "UTF-8")));
    610 
    611             String direction = "";
    612             String id = translit.getID();
    613             if (id.indexOf("Arabic") >= 0 || id.indexOf("Hebrew") >= 0) {
    614                 direction = " direction: rtl;";
    615             }
    616             boolean testRoundTrip = true;
    617             boolean generateSets = true;
    618             if (id.startsWith("Han-") || id.startsWith("ja-")) {
    619                 testRoundTrip = false;
    620                 generateSets = false;
    621             }
    622             out.println("<head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>");
    623             out.println("<style><!--");
    624             out.println("td, th       { vertical-align: top; border: 1px solid black }");
    625             out.println("td.s       { background-color: #EEEEEE;" + direction + " }");
    626             out.println("td.r       { background-color: #CCCCCC;" + direction + " }");
    627             out.println("td.n       { background-color: #FFFFCC; }");
    628             out.println("td.title       { border: 0px solid black}");
    629             out.println("span.d       { background-color: #FF6666 }");
    630             out.println("span.r       { background-color: #66FF66 }");
    631 
    632             out.println("body         { font-family: 'Arial Unicode MS', 'Lucida Sans Unicode', Arial, sans-serif; margin: 5 }");
    633             out.println("--></style>");
    634             out.println("<title>" + id + " Transliteration Check</title></head>");
    635             out.println("<body bgcolor='#FFFFFF'><p>See <a href='Test_Instructions.html'>Test_Instructions.html</a> for details.</p>");
    636             out.println("<table>");
    637 
    638             //out.println("<tr><th width='33%'>Thai</th><th width='33%'>Latin</th><th width='33%'>Thai</th></tr>");
    639 
    640             Transliterator tl = translit;
    641             Transliterator lt = tl.getInverse();
    642 
    643             Transliterator ltFilter = tl.getInverse();
    644             ltFilter.setFilter(new UnicodeSet("[:^Lu:]"));
    645             Transliterator tlFilter = lt.getInverse();
    646             tlFilter.setFilter(new UnicodeSet("[:^Lu:]"));
    647 
    648             //Transliterator.getInstance("[:^Lu:]" +  lt.getID());
    649 
    650             BreakIterator sentenceBreak = BreakIterator.getSentenceInstance();
    651 
    652             byte titleSetting = TITLELINE;
    653             //boolean upperfilter = false;
    654             boolean first = true;
    655             while (true) {
    656                 String line = in.readLine();
    657                 if (line == null) break;
    658                 line = line.trim();
    659                 if (line.length() == 0) continue;
    660                 if (line.charAt(0) == '\uFEFF') line = line.substring(1); // remove BOM
    661 
    662                 if (line.charAt(0) == '#') continue; // comments
    663 
    664                 if (line.equals("@TITLECASE@")) {
    665                     titleSetting = TITLEWORD;
    666                     out.println("<tr><td colspan='2' class='title'><b>Names</b></td></tr>");
    667                     continue;
    668                 } else if (line.equals("@UPPERFILTER@")) {
    669                     //upperfilter = true;
    670                     continue;
    671                 } else if (line.startsWith("@SET")) {
    672                     UnicodeSet s = new UnicodeSet(line.substring(4).trim());
    673                     out.println("<tr><td colspan='2' class='title'><b>Characters</b></td></tr>");
    674                     UnicodeSetIterator it = new UnicodeSetIterator(s);
    675                     while (it.next()) {
    676                         addSentenceToTable(out, it.codepoint != UnicodeSetIterator.IS_STRING
    677                             ? UTF16.valueOf(it.codepoint)
    678                             : it.string,
    679                             NONE, true, testRoundTrip, first, tl, lt);
    680                     }
    681                     continue;
    682                 }
    683 
    684                 sentenceBreak.setText(line);
    685                 int start = 0;
    686                 while (true) {
    687                     int end = sentenceBreak.next();
    688                     if (end == BreakIterator.DONE) break;
    689                     String coreSentence = line.substring(start, end);
    690                     //System.out.println("Core: " + hex.transliterate(coreSentence));
    691                     end = start;
    692 
    693                     int oldPos = 0;
    694                     while (oldPos < coreSentence.length()) {
    695                         // hack, because sentence doesn't seem to be working right
    696                         int pos = coreSentence.indexOf(". ", oldPos);
    697                         if (pos < 0) pos = coreSentence.length(); else pos = pos+2;
    698                         int pos2 = coreSentence.indexOf('\u3002', oldPos);
    699                         if (pos2 < 0) pos2 = coreSentence.length(); else pos2 = pos2 + 1;
    700                         if (pos > pos2) pos = pos2;
    701                         String sentence = coreSentence.substring(oldPos, pos).trim();
    702                         //System.out.println("Sentence: " + hex.transliterate(coreSentence));
    703                         oldPos = pos;
    704 
    705                         addSentenceToTable(out, sentence,
    706                             titleSetting, false, testRoundTrip, first, tl, lt);
    707 
    708                         first = false;
    709                     }
    710                 }
    711             }
    712             out.println("</table></body>");
    713             out.close();
    714 
    715             // Now write the source/target sets
    716             if (generateSets) {
    717                 outFile = new File(targetFile + "_Sets.html");
    718                 System.out.println("Writing: " + outFile.getCanonicalPath());
    719 
    720                 out = new PrintWriter(
    721                     new BufferedWriter(
    722                         new OutputStreamWriter(
    723                             new FileOutputStream(outFile), "UTF-8")));
    724                 out.println("<head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>");
    725                 out.println("<style><!--");
    726                 out.println("body         { font-family: 'Arial Unicode MS', 'Lucida Sans Unicode', Arial, sans-serif; margin: 5 }");
    727                 out.println("--></style>");
    728                 out.println("<title>" + id + " Transliteration Sets</title></head>");
    729                 out.println("<body bgcolor='#FFFFFF'>");
    730 
    731                 int dashPos = id.indexOf('-');
    732                 int slashPos = id.indexOf('/');
    733                 if (slashPos < 0) slashPos = id.length();
    734                 UnicodeSet sourceSuper = null;
    735                 try {
    736                     String temp = id.substring(0,dashPos);
    737                     if (temp.equals("ja")) sourceSuper = new UnicodeSet("[[:Han:][:hiragana:][:katakana:]]");
    738                     else sourceSuper = new UnicodeSet("[[:" + temp + ":][:Mn:][:Me:]]");
    739                 } catch (Exception e) {}
    740 
    741                 UnicodeSet targetSuper = null;
    742                 try {
    743                     targetSuper = new UnicodeSet("[[:" + id.substring(dashPos+1, slashPos) + ":][:Mn:][:Me:]]");
    744                 } catch (Exception e) {}
    745 
    746                 int nfdStyle = CLOSE_CASE | CLOSE_FLATTEN | CLOSE_CANONICAL;
    747                 int nfkdStyle = nfdStyle | CLOSE_COMPATIBILITY;
    748                 out.println("<ul>");
    749                 out.println("<p><b>None</b></p>");
    750                 showSets(out, translit, lt, null, null, 0);
    751                 out.println("<p><b>NFD</b></p>");
    752                 showSets(out, translit, lt, sourceSuper, targetSuper, nfdStyle);
    753                 out.println("<p><b>NFKD</b></p>");
    754                 showSets(out, translit, lt, sourceSuper, targetSuper, nfkdStyle);
    755                 out.println("</ul></body>");
    756                 out.close();
    757             }
    758             System.out.println("Done Writing");
    759         } catch (Exception e) {
    760             e.printStackTrace();
    761         } finally {
    762             if (in != null) {
    763                 try {
    764                     in.close();
    765                 } catch (Exception e) {
    766                     // ignore
    767                 }
    768             }
    769         }
    770     }
    771 
    772     static void addSentenceToTable(PrintWriter out, String sentence,
    773             byte titleSetting, boolean addName, boolean testRoundTrip, boolean first,
    774             Transliterator tl, Transliterator lt) {
    775         if (sentence.length() == 0) return; // skip empty lines
    776 
    777         String originalShow = sentence;
    778         String latin;
    779         latin = tl.transliterate(saveAscii.transliterate(sentence));
    780 
    781         String latinShow = latin;
    782         if (titleSetting == TITLEWORD) {
    783             latinShow = title.transliterate(latin);
    784         } else if (titleSetting == TITLELINE) {
    785             latinShow = titlecaseFirstWord(latinShow);
    786         }
    787         latinShow = restoreAscii.transliterate(latinShow);
    788 
    789         String reverse;
    790         reverse = restoreAscii.transliterate(lt.transliterate(latin));
    791 
    792         String NFKDSentence = Normalizer.normalize(sentence, Normalizer.NFKD);
    793         String NFKDLatin = Normalizer.normalize(latin, Normalizer.NFKD);
    794         String NFKDReverse = Normalizer.normalize(reverse, Normalizer.NFKD);
    795 
    796         if (latinShow.length() == 0) {
    797             latinShow = "<i>empty</i>";
    798         } else if (NFKDSentence.equals(NFKDLatin)) {
    799             latinShow = "<span class='r'>" + latinShow + "</span>";
    800         }
    801         String reverseShow = reverse;
    802 
    803         if (testRoundTrip && !NFKDReverse.equals(NFKDSentence)) {
    804             int minLen = reverse.length();
    805             if (minLen > sentence.length()) minLen = sentence.length();
    806             int i;
    807             for (i = 0; i < minLen; ++i) {
    808                 if (reverse.charAt(i) != sentence.charAt(i)) break;
    809             }
    810             //originalShow = sentence.substring(0,i) + "<span class='d'>" + sentence.substring(i) + "</span>";
    811             reverseShow = reverseShow.length() == 0
    812                 ? "<i>empty</i>"
    813                 //: reverse.substring(0,i) + "<span class='d'>" + reverse.substring(i) + "</span>";
    814                 : showDifference(sentence, reverse);
    815             out.println("<tr><td class='s'" + (first ? " width='50%'>" : ">") + originalShow
    816                 + "</td><td rowSpan='2'>" + latinShow
    817                 + "</td></tr><tr><td class='r'>" + reverseShow
    818                 + "</td></tr>");
    819         } else {
    820             out.println("<tr><td class='s'" + (first ? " width='50%'>" : ">") + originalShow
    821                 + "</td><td>" + latinShow
    822                 + "</td></tr>");
    823         }
    824         if (addName) {
    825             latinShow = hexAndName.transliterate(latin);
    826             if (latinShow.length() == 0) latinShow = "<i>empty</i>";
    827             originalShow = hexAndName.transliterate(sentence);
    828             if (originalShow.length() == 0) originalShow = "<i>empty</i>";
    829 
    830             out.println("<tr><td class='n'>" + originalShow
    831                 + "</td><td class='n'>" + latinShow
    832                 + "</td></tr>");
    833         }
    834         out.println("<tr><td></td></tr>");
    835 
    836     }
    837 
    838     static String showDifference(String as, String bs) {
    839         Differ differ = new Differ(300, 3);
    840         StringBuffer out = new StringBuffer();
    841         int max = as.length();
    842         if (max < bs.length()) max = bs.length();
    843         for (int j = 0; j <= max; ++j) {
    844             if (j < as.length()) differ.addA(as.substring(j, j+1));
    845             if (j < bs.length()) differ.addB(bs.substring(j, j+1));
    846             differ.checkMatch(j == max);
    847 
    848             if (differ.getACount() != 0 || differ.getBCount() != 0) {
    849                 out.append("...");
    850                 if (differ.getACount() != 0) {
    851                     out.append("<span class='r'>");
    852                     for (int i = 0; i < differ.getACount(); ++i) {
    853                         out.append(differ.getA(i));
    854                     }
    855                     out.append("</span>");
    856                 }
    857                 if (differ.getBCount() != 0) {
    858                     out.append("<span class='d'>");
    859                     for (int i = 0; i < differ.getBCount(); ++i) {
    860                         out.append(differ.getB(i));
    861                     }
    862                     out.append("</span>");
    863                 }
    864                 out.append("...");
    865             }
    866         }
    867         return out.toString();
    868     }
    869 
    870     static void showSets(PrintWriter out, Transliterator translit, Transliterator inverse,
    871       UnicodeSet sourceSuper, UnicodeSet targetSuper, int options) {
    872         out.println("<li>Source Set:<ul><li>" +         toPattern(closeUnicodeSet(translit.getSourceSet(), options), sourceSuper) + "</li></ul></li>");
    873         out.println("<li>Reverse Target Set:<ul><li>" + toPattern(closeUnicodeSet(inverse.getTargetSet(),  options), sourceSuper) + "</li></ul></li>");
    874         out.println("<li>Target Set:<ul><li>" +         toPattern(closeUnicodeSet(translit.getTargetSet(), options), targetSuper) + "</li></ul></li>");
    875         out.println("<li>Reverse Source Set:<ul><li>" + toPattern(closeUnicodeSet(inverse.getSourceSet(),  options), targetSuper) + "</li></ul></li>");
    876     }
    877 
    878     static final int CLOSE_CASE = 1, CLOSE_FLATTEN = 2, CLOSE_CANONICAL = 4, CLOSE_COMPATIBILITY = 8;
    879 
    880     static UnicodeSet closeUnicodeSet(UnicodeSet source, int options) {
    881         if (options == 0) return source;
    882 
    883         UnicodeSetIterator it = new UnicodeSetIterator(source);
    884         UnicodeSet additions = new UnicodeSet(); // to avoid messing up iterator
    885         UnicodeSet removals = new UnicodeSet(); // to avoid messing up iterator
    886         String base;
    887         int cp;
    888 
    889         // Add all case equivalents
    890         if ((options & CLOSE_CASE) != 0) {
    891             while (it.next()) {
    892                 cp = it.codepoint;
    893                 if (cp == UnicodeSetIterator.IS_STRING) continue;
    894                 int type = UCharacter.getType(cp);
    895                 if (type == Character.UPPERCASE_LETTER || type == Character.LOWERCASE_LETTER || type == Character.TITLECASE_LETTER) {
    896                     additions.add(UCharacter.toLowerCase(UTF16.valueOf(cp)));
    897                     additions.add(UCharacter.toUpperCase(UTF16.valueOf(cp)));
    898                 }
    899             }
    900             source.addAll(additions);
    901         }
    902 
    903         // Add the canonical closure of all strings and characters in source
    904         if ((options & CLOSE_CANONICAL) != 0) {
    905             it.reset();
    906             additions.clear();
    907             CanonicalIterator ci = new CanonicalIterator(".");
    908             while (it.next()) {
    909                 if (it.codepoint == UnicodeSetIterator.IS_STRING) base = it.string;
    910                 else base = UTF16.valueOf(it.codepoint);
    911                 ci.setSource(base);
    912                 while (true) {
    913                     String trial = ci.next();
    914                     if (trial == null) break;
    915                     if (trial.equals(base)) continue;
    916                     additions.add(trial);
    917                 }
    918             }
    919             source.addAll(additions);
    920         }
    921 
    922         // flatten strings
    923         if ((options & CLOSE_FLATTEN) != 0) {
    924             it.reset();
    925             additions.clear();
    926             while (it.next()) {
    927                 if (it.codepoint != UnicodeSetIterator.IS_STRING) continue;
    928                 additions.addAll(it.string);
    929                 removals.add(it.string);
    930                 //System.out.println("flattening '" + hex.transliterate(it.string) + "'");
    931             }
    932             source.addAll(additions);
    933             source.removeAll(removals);
    934         }
    935 
    936         // Now add decompositions of characters in source
    937         if ((options & CLOSE_COMPATIBILITY) != 0) {
    938             it.reset(source);
    939             additions.clear();
    940             while (it.next()) {
    941                 if (it.codepoint == UnicodeSetIterator.IS_STRING) base = it.string;
    942                 else base = UTF16.valueOf(it.codepoint);
    943                 if (Normalizer.isNormalized(base, Normalizer.NFKD,0)) continue;
    944                 String decomp = Normalizer.normalize(base, Normalizer.NFKD);
    945                 additions.add(decomp);
    946             }
    947             source.addAll(additions);
    948 
    949             // Now add any other character that decomposes to a character in source
    950             for (cp = 0; cp < 0x10FFFF; ++cp) {
    951                 if (!UCharacter.isDefined(cp)) continue;
    952                 if (Normalizer.isNormalized(cp, Normalizer.NFKD,0)) continue;
    953                 if (source.contains(cp)) continue;
    954 
    955                 String decomp = Normalizer.normalize(cp, Normalizer.NFKD);
    956                 if (source.containsAll(decomp)) {
    957                     // System.out.println("Adding: " + Integer.toString(cp,16) + " " + UCharacter.getName(cp));
    958                     source.add(cp);
    959                 }
    960             }
    961         }
    962 
    963         return source;
    964     }
    965 
    966     static String toPattern(UnicodeSet source, UnicodeSet superset) {
    967         if (superset != null) {
    968             source.removeAll(superset);
    969             return "[" + superset.toPattern(true) + " " + source.toPattern(true) + "]";
    970         }
    971         return source.toPattern(true);
    972     }
    973 
    974     static BreakIterator bi = BreakIterator.getWordInstance();
    975 
    976     static String titlecaseFirstWord(String line) {
    977         // search for first word with letters. If the first letter is lower, then titlecase it.
    978         bi.setText(line);
    979         int start = 0;
    980         while (true) {
    981             int end = bi.next();
    982             if (end == BreakIterator.DONE) break;
    983             int firstLetterType = getFirstLetterType(line, start, end);
    984             if (firstLetterType != Character.UNASSIGNED) {
    985                 if (firstLetterType != Character.LOWERCASE_LETTER) break;
    986                 line = line.substring(0, start)
    987                     + UCharacter.toTitleCase(line.substring(start, end), bi)
    988                     + line.substring(end);
    989                 break;
    990             }
    991             end = start;
    992         }
    993         return line;
    994     }
    995 
    996     static final int LETTER_MASK =
    997           (1<<Character.UPPERCASE_LETTER)
    998         | (1<<Character.LOWERCASE_LETTER)
    999         | (1<<Character.TITLECASE_LETTER)
   1000         | (1<<Character.MODIFIER_LETTER)
   1001         | (1<<Character.OTHER_LETTER)
   1002         ;
   1003 
   1004     static int getFirstLetterType(String line, int start, int end) {
   1005         int cp;
   1006         for (int i = start; i < end; i += UTF16.getCharCount(cp)) {
   1007             cp = UTF16.charAt(line, i);
   1008             int type = UCharacter.getType(cp);
   1009             if (((1<<type) & LETTER_MASK) != 0) return type;
   1010         }
   1011         return Character.UNASSIGNED;
   1012     }
   1013 
   1014     static void printNames(UnicodeSet s, String targetFile) {
   1015         try {
   1016             File outFile = new File(targetFile);
   1017             System.out.println("Writing: " + outFile.getCanonicalPath());
   1018 
   1019             PrintWriter out = new PrintWriter(
   1020                 new BufferedWriter(
   1021                     new OutputStreamWriter(
   1022                         new FileOutputStream(outFile), "UTF-8")));
   1023             UnicodeSet main = new UnicodeSet();
   1024 
   1025             UnicodeSet others = new UnicodeSet();
   1026             UnicodeSetIterator it = new UnicodeSetIterator(s);
   1027             while (it.next()) {
   1028                 if (!UCharacter.isDefined(it.codepoint)) continue;
   1029                 if (!Normalizer.isNormalized(it.codepoint, Normalizer.NFD,0)) {
   1030                     String decomp = Normalizer.normalize(it.codepoint, Normalizer.NFD);
   1031                     others.addAll(decomp);
   1032                     continue;
   1033                 }
   1034                 out.println(" " + UTF16.valueOf(it.codepoint) + " <> XXX # " + UCharacter.getName(it.codepoint));
   1035                 main.add(it.codepoint);
   1036             }
   1037 
   1038             if (others.size() != 0) {
   1039                 out.println("Decomposed characters found above: ");
   1040                 others.removeAll(main);
   1041                 it.reset(others);
   1042                 while (it.next()) {
   1043                     out.println(" " + UTF16.valueOf(it.codepoint) + " <> XXX # " + UCharacter.getName(it.codepoint));
   1044                 }
   1045             }
   1046 
   1047             out.close();
   1048             System.out.println("Done Writing");
   1049         } catch (Exception e) {
   1050             e.printStackTrace();
   1051         }
   1052     }
   1053 
   1054     static Transliterator hex = Transliterator.getInstance("[^\\u0020-\\u007E] hex");
   1055     static final String saveRules =
   1056           "A <> \uEA41; B <> \uEA42; C <> \uEA43; D <> \uEA44; E <> \uEA45; F <> \uEA46; G <> \uEA47; H <> \uEA48; I <> \uEA49; "
   1057         + "J <> \uEA4A; K <> \uEA4B; L <> \uEA4C; M <> \uEA4D; N <> \uEA4E; O <> \uEA4F; P <> \uEA50; Q <> \uEA51; R <> \uEA52; "
   1058         + "S <> \uEA53; T <> \uEA54; U <> \uEA55; V <> \uEA56; W <> \uEA57; X <> \uEA58; Y <> \uEA59; Z <> \uEA5A; "
   1059         + "a <> \uEA61; b <> \uEA62; c <> \uEA63; d <> \uEA64; e <> \uEA65; f <> \uEA66; g <> \uEA67; h <> \uEA68; i <> \uEA69; "
   1060         + "j <> \uEA6A; k <> \uEA6B; l <> \uEA6C; m <> \uEA6D; n <> \uEA6E; o <> \uEA6F; p <> \uEA70; q <> \uEA71; r <> \uEA72; "
   1061         + "s <> \uEA73; t <> \uEA74; u <> \uEA75; v <> \uEA76; w <> \uEA77; x <> \uEA78; y <> \uEA79; z <> \uEA7A;";
   1062 
   1063     static Transliterator saveAscii = Transliterator.createFromRules("ascii-saved", saveRules, Transliterator.FORWARD);
   1064     static Transliterator restoreAscii = Transliterator.createFromRules("ascii-saved", saveRules, Transliterator.REVERSE);
   1065 
   1066     static {
   1067 
   1068         if (false) {
   1069 
   1070         for (char i = 'A'; i <= 'z'; ++i) {
   1071             System.out.print(i + " <> " + hex.transliterate(String.valueOf((char)(0xEA00 + i))) + "; ");
   1072         }
   1073 
   1074         UnicodeSet x = new UnicodeSet("[[:^ccc=0:]&[:^ccc=230:]]");
   1075         x = x.complement();
   1076         x = x.complement();
   1077         System.out.println("Test: " + x.toPattern(true));
   1078 
   1079         Transliterator y = Transliterator.createFromRules("xxx", "$notAbove = [[:^ccc=0:]&[:^ccc=230:]]; u ($notAbove*) \u0308 > XXX | $1; ", Transliterator.FORWARD);
   1080 
   1081         String[] testList = {"u\u0308", "u\u0316\u0308", "u\u0308\u0316", "u\u0301\u0308", "u\u0308\u0301"};
   1082         for (int i = 0; i < testList.length; ++i) {
   1083             String yy = y.transliterate(testList[i]);
   1084             System.out.println(hex.transliterate(testList[i]) + " => " + hex.transliterate(yy));
   1085         }
   1086 
   1087         //printNames(new UnicodeSet("[\u0600-\u06FF]"), "Arabic-Latin.txt");
   1088 
   1089 
   1090         /*
   1091         BreakTransliterator.register();
   1092 
   1093         BreakTransliterator testTrans = new BreakTransliterator("Any-XXX", null, null, "$");
   1094         String testSource = "The Quick:   Brown fox--jumped.";
   1095         BreakIterator bi = testTrans.getBreakIterator();
   1096         bi.setText(new StringCharacterIterator(testSource));
   1097         printBreaks(0, testSource, bi);
   1098         //bi.setText(UCharacterIterator.getInstance(testSource));
   1099         //printBreaks(1, testSource, bi);
   1100 
   1101         printIteration(2, testSource, new StringCharacterIterator(testSource));
   1102         //printIteration(3, testSource, UCharacterIterator.getInstance(testSource));
   1103 
   1104 
   1105 
   1106         String test = testTrans.transliterate(testSource);
   1107         System.out.println("Test3: " + test);
   1108         DummyFactory.add(testTrans.getID(), testTrans);
   1109         */
   1110 
   1111         // AnyTransliterator.ScriptRunIterator.registerAnyToScript();
   1112 
   1113         AnyTransliterator at = new AnyTransliterator("Greek", null);
   1114         at.transliterate("(cat,\u03b1,\u0915)");
   1115         DummyFactory.add(at.getID(), at);
   1116 
   1117         at = new AnyTransliterator("Devanagari", null);
   1118         at.transliterate("(cat,\u03b1,\u0915)");
   1119         DummyFactory.add(at.getID(), at);
   1120 
   1121         at = new AnyTransliterator("Latin", null);
   1122         at.transliterate("(cat,\u03b1,\u0915)");
   1123         DummyFactory.add(at.getID(), at);
   1124 
   1125         DummyFactory.add("Any-gif", Transliterator.createFromRules("gif", "'\\'u(..)(..) > '<img src=\"http://www.unicode.org/gifs/24/' $1 '/U' $1$2 '.gif\">';", Transliterator.FORWARD));
   1126         DummyFactory.add("gif-Any", Transliterator.getInstance("Any-Null"));
   1127 
   1128         DummyFactory.add("Any-RemoveCurly", Transliterator.createFromRules("RemoveCurly", "[\\{\\}] > ;", Transliterator.FORWARD));
   1129         DummyFactory.add("RemoveCurly-Any", Transliterator.getInstance("Any-Null"));
   1130 
   1131         System.out.println("Trying &hex");
   1132         Transliterator t = Transliterator.createFromRules("hex2", "(.) > &hex($1);", Transliterator.FORWARD);
   1133         System.out.println("Registering");
   1134         DummyFactory.add("Any-hex2", t);
   1135 
   1136         System.out.println("Trying &gif");
   1137         t = Transliterator.createFromRules("gif2", "(.) > &any-gif($1);", Transliterator.FORWARD);
   1138         System.out.println("Registering");
   1139         DummyFactory.add("Any-gif2", t);
   1140         }
   1141     }
   1142 
   1143 
   1144     void setTransliterator(String name, String id) {
   1145         if (DEBUG) System.out.println("Got: " + name);
   1146         if (id == null) {
   1147             translit = Transliterator.getInstance(name);
   1148         } else {
   1149             String reverseId = "";
   1150             int pos = id.indexOf('-');
   1151             if (pos < 0) {
   1152                 reverseId = id + "-Any";
   1153                 id = "Any-" + id;
   1154             } else {
   1155                 int pos2 = id.indexOf("/", pos);
   1156                 if (pos2 < 0) {
   1157                     reverseId = id.substring(pos+1) + "-" + id.substring(0,pos);
   1158                 } else {
   1159                     reverseId = id.substring(pos+1, pos2) + "-" + id.substring(0,pos) + id.substring(pos2);
   1160                 }
   1161             }
   1162 
   1163 
   1164             translit = Transliterator.createFromRules(id, name, Transliterator.FORWARD);
   1165             if (DEBUG) {
   1166                 System.out.println("***Forward Rules");
   1167                 System.out.println(translit.toRules(true));
   1168                 System.out.println("***Source Set");
   1169                 System.out.println(translit.getSourceSet().toPattern(true));
   1170             }
   1171                 System.out.println("***Target Set");
   1172                 UnicodeSet target = translit.getTargetSet();
   1173                 System.out.println(target.toPattern(true));
   1174                 UnicodeSet rest = new UnicodeSet("[a-z]").removeAll(target);
   1175                 System.out.println("***ASCII - Target Set");
   1176                 System.out.println(rest.toPattern(true));
   1177 
   1178             DummyFactory.add(id, translit);
   1179 
   1180             Transliterator translit2 = Transliterator.createFromRules(reverseId, name, Transliterator.REVERSE);
   1181             if (DEBUG) {
   1182                 System.out.println("***Backward Rules");
   1183                 System.out.println(translit2.toRules(true));
   1184             }
   1185             DummyFactory.add(reverseId, translit2);
   1186 
   1187             Transliterator rev = translit.getInverse();
   1188             if (DEBUG) System.out.println("***Inverse Rules");
   1189             if (DEBUG) System.out.println(rev.toRules(true));
   1190 
   1191         }
   1192         text.flush();
   1193         text.setTransliterator(translit);
   1194         convertSelectionItem.setLabel(Transliterator.getDisplayName(translit.getID()));
   1195 
   1196         addHistory(translit);
   1197 
   1198         Transliterator inv;
   1199         try {
   1200             inv = translit.getInverse();
   1201         } catch (Exception ex) {
   1202             inv = null;
   1203         }
   1204         if (inv != null) {
   1205             addHistory(inv);
   1206             swapSelectionItem.setEnabled(true);
   1207         } else {
   1208             swapSelectionItem.setEnabled(false);
   1209         }
   1210         System.out.println("Set transliterator: " + translit.getID()
   1211             + (inv != null ? " and " + inv.getID() : ""));
   1212     }
   1213 
   1214     void addHistory(Transliterator trans) {
   1215         String name = trans.getID();
   1216         MenuItem cmi = (MenuItem) historyMap.get(name);
   1217         if (cmi == null) {
   1218             cmi = new MenuItem(Transliterator.getDisplayName(name));
   1219             cmi.addActionListener(new TransliterationListener(name));
   1220             historyMap.put(name, cmi);
   1221             historySet.add(cmi);
   1222             historyMenu.removeAll();
   1223             Iterator it = historySet.iterator();
   1224             while (it.hasNext()) {
   1225                 historyMenu.add((MenuItem)it.next());
   1226             }
   1227         }
   1228     }
   1229 
   1230     class TransliterationListener implements ActionListener, ItemListener {
   1231         String name;
   1232         public TransliterationListener(String name) {
   1233             this.name = name;
   1234         }
   1235         public void actionPerformed(ActionEvent e) {
   1236             setTransliterator(name, null);
   1237         }
   1238         public void itemStateChanged(ItemEvent e) {
   1239             if (e.getStateChange() == ItemEvent.SELECTED) {
   1240                 setTransliterator(name, null);
   1241             } else {
   1242                 setTransliterator("Any-Null", null);
   1243             }
   1244         }
   1245     }
   1246 
   1247     class FontActionListener implements ActionListener {
   1248         String name;
   1249         public FontActionListener(String name) {
   1250             this.name = name;
   1251         }
   1252         public void actionPerformed(ActionEvent e) {
   1253             if (DEBUG) System.out.println("Font: " + name);
   1254             fontName = name;
   1255             text.setFont(new Font(fontName, Font.PLAIN, fontSize));
   1256         }
   1257     }
   1258 
   1259     class SizeActionListener implements ActionListener {
   1260         int size;
   1261         public SizeActionListener(int size) {
   1262             this.size = size;
   1263         }
   1264         public void actionPerformed(ActionEvent e) {
   1265             if (DEBUG) System.out.println("Size: " + size);
   1266             fontSize = size;
   1267             text.setFont(new Font(fontName, Font.PLAIN, fontSize));
   1268         }
   1269     }
   1270 
   1271     Set add(Set s, Enumeration enumeration) {
   1272         while(enumeration.hasMoreElements()) {
   1273             s.add(enumeration.nextElement());
   1274         }
   1275         return s;
   1276     }
   1277 
   1278     /**
   1279      * Get a sorted list of the system transliterators.
   1280      */
   1281      /*
   1282     private static Vector getSystemTransliteratorNames() {
   1283         Vector v = new Vector();
   1284         for (Enumeration e=Transliterator.getAvailableIDs();
   1285              e.hasMoreElements(); ) {
   1286             v.addElement(e.nextElement());
   1287         }
   1288         // Insertion sort, O(n^2) acceptable for small n
   1289         for (int i=0; i<(v.size()-1); ++i) {
   1290             String a = (String) v.elementAt(i);
   1291             for (int j=i+1; j<v.size(); ++j) {
   1292                 String b = (String) v.elementAt(j);
   1293                 if (a.compareTo(b) > 0) {
   1294                     v.setElementAt(b, i);
   1295                     v.setElementAt(a, j);
   1296                     a = b;
   1297                 }
   1298             }
   1299         }
   1300         return v;
   1301     }
   1302     */
   1303 
   1304 /*
   1305     private void setNoTransliterator() {
   1306         translitItem = noTranslitItem;
   1307         noTranslitItem.setState(true);
   1308         handleSetTransliterator(noTranslitItem.getLabel());
   1309         compound = false;
   1310         for (int i=0; i<translitMenu.getItemCount(); ++i) {
   1311             MenuItem it = translitMenu.getItem(i);
   1312             if (it != noTranslitItem && it instanceof CheckboxMenuItem) {
   1313                 ((CheckboxMenuItem) it).setState(false);
   1314             }
   1315         }
   1316     }
   1317 */
   1318 /*
   1319     private void handleAddToCompound(String name) {
   1320         if (compoundCount < MAX_COMPOUND) {
   1321             compoundTranslit[compoundCount] = decodeTranslitItem(name);
   1322             ++compoundCount;
   1323             Transliterator t[] = new Transliterator[compoundCount];
   1324             System.arraycopy(compoundTranslit, 0, t, 0, compoundCount);
   1325             translit = new CompoundTransliterator(t);
   1326             text.setTransliterator(translit);
   1327         }
   1328     }
   1329 */
   1330 /*
   1331     private void handleSetTransliterator(String name) {
   1332         translit = decodeTranslitItem(name);
   1333         text.setTransliterator(translit);
   1334     }
   1335     */
   1336 
   1337     /**
   1338      * Decode a menu item that looks like <translit name>.
   1339      */
   1340      /*
   1341     private static Transliterator decodeTranslitItem(String name) {
   1342         return (name.equals(NO_TRANSLITERATOR))
   1343             ? null : Transliterator.getInstance(name);
   1344     }
   1345     */
   1346 
   1347     private void handleBatchTransliterate(Transliterator trans) {
   1348         if (trans == null) {
   1349             return;
   1350         }
   1351 
   1352         int start = text.getSelectionStart();
   1353         int end = text.getSelectionEnd();
   1354         ReplaceableString s =
   1355             new ReplaceableString(text.getText().substring(start, end));
   1356 
   1357         StringBuffer log = null;
   1358         if (DEBUG) {
   1359             log = new StringBuffer();
   1360             log.append('"' + s.toString() + "\" (start " + start +
   1361                        ", end " + end + ") -> \"");
   1362         }
   1363 
   1364         trans.transliterate(s);
   1365         String str = s.toString();
   1366 
   1367         if (DEBUG) {
   1368             log.append(str + "\"");
   1369             System.out.println("Batch " + trans.getID() + ": " + log.toString());
   1370         }
   1371 
   1372         text.replaceRange(str, start, end);
   1373         text.select(start, start + str.length());
   1374     }
   1375 
   1376     private void handleClose() {
   1377         helpDialog.dispose();
   1378         dispose();
   1379     }
   1380 
   1381     /*
   1382     class InfoDialog extends Dialog {
   1383         protected Button button;
   1384         protected TextArea area;
   1385         protected Dialog me;
   1386         protected Panel bottom;
   1387 
   1388         public TextArea getArea() {
   1389             return area;
   1390         }
   1391 
   1392         public Panel getBottom() {
   1393             return bottom;
   1394         }
   1395 
   1396         InfoDialog(Frame parent, String title, String label, String message) {
   1397             super(parent, title, false);
   1398             me = this;
   1399             this.setLayout(new BorderLayout());
   1400             if (label.length() != 0) {
   1401                 this.add("North", new Label(label));
   1402             }
   1403 
   1404             area = new TextArea(message, 8, 80, TextArea.SCROLLBARS_VERTICAL_ONLY);
   1405             this.add("Center", area);
   1406 
   1407             button = new Button("Hide");
   1408             button.addActionListener(new ActionListener() {
   1409                 public void actionPerformed(ActionEvent e) {
   1410                     me.hide();
   1411                 }
   1412             });
   1413             bottom = new Panel();
   1414             bottom.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
   1415             bottom.add(button);
   1416             this.add("South", bottom);
   1417             this.pack();
   1418             addWindowListener(new WindowAdapter() {
   1419                 public void windowClosing(WindowEvent e) {
   1420                     me.hide();
   1421                 }
   1422             });
   1423         }
   1424     }
   1425     */
   1426 }
   1427