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 
main(String[] args)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 
Demo(int width, int height)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 
initMenus()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 
FileListener(Demo frame, int choice)425         FileListener(Demo frame, int choice) {
426             this.frame = frame;
427             this.choice = choice;
428         }
429 
actionPerformed(ActionEvent e)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
add(String ID, Transliterator t)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         }
getInstance(String ID)530         public Transliterator getInstance(String ID) {
531             return (Transliterator) m.get(ID);
532         }
533     }
534 
printBreaks(int num, String testSource, BreakIterator brkItr)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 
printIteration(int num, String testSource, CharacterIterator ci)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 
printSources()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 
genTestFile(File sourceFile, Transliterator translit, String variant)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 
addSentenceToTable(PrintWriter out, String sentence, byte titleSetting, boolean addName, boolean testRoundTrip, boolean first, Transliterator tl, Transliterator lt)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 
showDifference(String as, String bs)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 
showSets(PrintWriter out, Transliterator translit, Transliterator inverse, UnicodeSet sourceSuper, UnicodeSet targetSuper, int options)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 
closeUnicodeSet(UnicodeSet source, int options)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 
toPattern(UnicodeSet source, UnicodeSet superset)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 
titlecaseFirstWord(String line)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 
getFirstLetterType(String line, int start, int end)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 
printNames(UnicodeSet s, String targetFile)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)");
at.getID()1125         DummyFactory.add(at.getID(), at);
1126 
1127         at = new AnyTransliterator("Devanagari", null);
1128         at.transliterate("(cat,\u03b1,\u0915)");
at.getID()1129         DummyFactory.add(at.getID(), at);
1130 
1131         at = new AnyTransliterator("Latin", null);
1132         at.transliterate("(cat,\u03b1,\u0915)");
at.getID()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 
setTransliterator(String name, String id)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 
addHistory(Transliterator trans)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;
TransliterationListener(String name)1242         public TransliterationListener(String name) {
1243             this.name = name;
1244         }
actionPerformed(ActionEvent e)1245         public void actionPerformed(ActionEvent e) {
1246             setTransliterator(name, null);
1247         }
itemStateChanged(ItemEvent e)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;
FontActionListener(String name)1259         public FontActionListener(String name) {
1260             this.name = name;
1261         }
actionPerformed(ActionEvent e)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;
SizeActionListener(int size)1271         public SizeActionListener(int size) {
1272             this.size = size;
1273         }
actionPerformed(ActionEvent e)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 
add(Set s, Enumeration enumeration)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 
handleBatchTransliterate(Transliterator trans)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 
handleClose()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