1/*
2 *******************************************************************************
3 * Copyright (C) 1996-2012, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.demo.translit;
8
9import java.awt.Button;
10import java.awt.CheckboxMenuItem;
11import java.awt.FileDialog;
12import java.awt.Font;
13import java.awt.Frame;
14import java.awt.GraphicsEnvironment;
15import java.awt.Label;
16import java.awt.Menu;
17import java.awt.MenuBar;
18import java.awt.MenuItem;
19import java.awt.MenuShortcut;
20import java.awt.TextField;
21import java.awt.event.ActionEvent;
22import java.awt.event.ActionListener;
23import java.awt.event.ItemEvent;
24import java.awt.event.ItemListener;
25import java.awt.event.KeyEvent;
26import java.awt.event.WindowAdapter;
27import java.awt.event.WindowEvent;
28import java.io.BufferedReader;
29import java.io.BufferedWriter;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileOutputStream;
33import java.io.InputStreamReader;
34import java.io.OutputStreamWriter;
35import java.io.PrintWriter;
36import java.text.CharacterIterator;
37import java.util.Comparator;
38import java.util.Enumeration;
39import java.util.HashMap;
40import java.util.Iterator;
41import java.util.Map;
42import java.util.Set;
43import java.util.TreeSet;
44
45import com.ibm.icu.impl.Differ;
46import com.ibm.icu.lang.UCharacter;
47import com.ibm.icu.text.BreakIterator;
48import com.ibm.icu.text.CanonicalIterator;
49import com.ibm.icu.text.Normalizer;
50import com.ibm.icu.text.ReplaceableString;
51import com.ibm.icu.text.Transliterator;
52import com.ibm.icu.text.UTF16;
53import com.ibm.icu.text.UnicodeSet;
54import 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 */
66public 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