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