]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/translit/src/com/ibm/icu/dev/test/util/BagFormatter.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / translit / src / com / ibm / icu / dev / test / util / BagFormatter.java
1 /*\r
2  *******************************************************************************\r
3  * Copyright (C) 2002-2010, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.dev.test.util;\r
8 \r
9 import java.io.BufferedReader;\r
10 import java.io.BufferedWriter;\r
11 import java.io.File;\r
12 import java.io.FileInputStream;\r
13 import java.io.FileOutputStream;\r
14 import java.io.IOException;\r
15 import java.io.InputStreamReader;\r
16 import java.io.OutputStreamWriter;\r
17 import java.io.PrintWriter;\r
18 import java.io.StringWriter;\r
19 import java.text.MessageFormat;\r
20 import java.util.Collection;\r
21 import java.util.HashMap;\r
22 import java.util.HashSet;\r
23 import java.util.Locale;\r
24 import java.util.Map;\r
25 \r
26 import com.ibm.icu.impl.Utility;\r
27 import com.ibm.icu.text.NumberFormat;\r
28 import com.ibm.icu.text.Transliterator;\r
29 import com.ibm.icu.text.UTF16;\r
30 import com.ibm.icu.text.UnicodeSet;\r
31 \r
32 public class BagFormatter {\r
33     static final boolean DEBUG = false;\r
34     public static final boolean SHOW_FILES;\r
35     static {\r
36     boolean showFiles = false;\r
37     try {\r
38         showFiles = System.getProperty("SHOW_FILES") != null;\r
39     }\r
40     catch (SecurityException e) {\r
41     }\r
42     SHOW_FILES = showFiles;\r
43     }\r
44 \r
45     public static final PrintWriter CONSOLE = new PrintWriter(System.out,true);\r
46 \r
47     private static PrintWriter log = CONSOLE;\r
48 \r
49     private boolean abbreviated = false;\r
50     private String separator = ",";\r
51     private String prefix = "[";\r
52     private String suffix = "]";\r
53     private UnicodeProperty.Factory source;\r
54     private UnicodeLabel nameSource;\r
55     private UnicodeLabel labelSource;\r
56     private UnicodeLabel rangeBreakSource;\r
57     private UnicodeLabel valueSource;\r
58     private String propName = "";\r
59     private boolean showCount = true;\r
60     //private boolean suppressReserved = true;\r
61     private boolean hexValue = false;\r
62     private static final String NULL_VALUE = "_NULL_VALUE_";\r
63     private int fullTotal = -1;\r
64     private boolean showTotal = true;\r
65     private String lineSeparator = "\r\n";\r
66     private Tabber tabber = new Tabber.MonoTabber();\r
67 \r
68     /**\r
69      * Compare two UnicodeSets, and show the differences\r
70      * @param name1 name of first set to be compared\r
71      * @param set1 first set\r
72      * @param name2 name of second set to be compared\r
73      * @param set2 second set\r
74      * @return formatted string\r
75      */\r
76     public String showSetDifferences(\r
77         String name1,\r
78         UnicodeSet set1,\r
79         String name2,\r
80         UnicodeSet set2) {\r
81 \r
82         StringWriter result = new StringWriter();\r
83         showSetDifferences(new PrintWriter(result),name1,set1,name2,set2);\r
84         result.flush();\r
85         return result.getBuffer().toString();\r
86     }\r
87 \r
88     public String showSetDifferences(\r
89         String name1,\r
90         Collection set1,\r
91         String name2,\r
92         Collection set2) {\r
93 \r
94         StringWriter result = new StringWriter();\r
95         showSetDifferences(new PrintWriter(result), name1, set1, name2, set2);\r
96         result.flush();\r
97         return result.getBuffer().toString();\r
98     }\r
99 \r
100     public void showSetDifferences(\r
101             PrintWriter pw,\r
102             String name1,\r
103             UnicodeSet set1,\r
104             String name2,\r
105             UnicodeSet set2) {\r
106         showSetDifferences(pw, name1, set1, name2, set2, -1);\r
107     }\r
108     /**\r
109      * Compare two UnicodeSets, and show the differences\r
110      * @param name1 name of first set to be compared\r
111      * @param set1 first set\r
112      * @param name2 name of second set to be compared\r
113      * @param set2 second set\r
114      */\r
115     public void showSetDifferences(\r
116         PrintWriter pw,\r
117         String name1,\r
118         UnicodeSet set1,\r
119         String name2,\r
120         UnicodeSet set2,\r
121         int flags) \r
122     {\r
123         if (pw == null) pw = CONSOLE;\r
124         String[] names = { name1, name2 };\r
125 \r
126         UnicodeSet temp;\r
127         \r
128         if ((flags&1) != 0) {\r
129             temp = new UnicodeSet(set1).removeAll(set2);\r
130             pw.print(lineSeparator);\r
131             pw.print(inOut.format(names));\r
132             pw.print(lineSeparator);\r
133             showSetNames(pw, temp);\r
134         }\r
135 \r
136         if ((flags&2) != 0) {\r
137             temp = new UnicodeSet(set2).removeAll(set1);\r
138             pw.print(lineSeparator);\r
139             pw.print(outIn.format(names));\r
140             pw.print(lineSeparator);\r
141             showSetNames(pw, temp);\r
142         }\r
143 \r
144         if ((flags&4) != 0) {\r
145             temp = new UnicodeSet(set2).retainAll(set1);\r
146             pw.print(lineSeparator);\r
147             pw.print(inIn.format(names));\r
148             pw.print(lineSeparator);\r
149             showSetNames(pw, temp);\r
150         }\r
151         pw.flush();\r
152     }\r
153 \r
154     public void showSetDifferences(\r
155         PrintWriter pw,\r
156         String name1,\r
157         Collection set1,\r
158         String name2,\r
159         Collection set2) {\r
160 \r
161         if (pw == null) pw = CONSOLE;\r
162         String[] names = { name1, name2 };\r
163         // damn'd collection doesn't have a clone, so\r
164         // we go with Set, even though that\r
165         // may not preserve order and duplicates\r
166         Collection temp = new HashSet(set1);\r
167         temp.removeAll(set2);\r
168         pw.println();\r
169         pw.println(inOut.format(names));\r
170         showSetNames(pw, temp);\r
171 \r
172         temp.clear();\r
173         temp.addAll(set2);\r
174         temp.removeAll(set1);\r
175         pw.println();\r
176         pw.println(outIn.format(names));\r
177         showSetNames(pw, temp);\r
178 \r
179         temp.clear();\r
180         temp.addAll(set1);\r
181         temp.retainAll(set2);\r
182         pw.println();\r
183         pw.println(inIn.format(names));\r
184         showSetNames(pw, temp);\r
185     }\r
186 \r
187     /**\r
188      * Returns a list of items in the collection, with each separated by the separator.\r
189      * Each item must not be null; its toString() is called for a printable representation\r
190      * @param c source collection\r
191      * @return a String representation of the list\r
192      */\r
193     public String showSetNames(Object c) {\r
194         StringWriter buffer = new StringWriter();\r
195         PrintWriter output = new PrintWriter(buffer);\r
196         showSetNames(output,c);\r
197         return buffer.toString();\r
198     }\r
199 \r
200     /**\r
201      * Returns a list of items in the collection, with each separated by the separator.\r
202      * Each item must not be null; its toString() is called for a printable representation\r
203      * @param output destination to which to write names\r
204      * @param c source collection\r
205      */\r
206     public void showSetNames(PrintWriter output, Object c) {\r
207         mainVisitor.doAt(c, output);\r
208         output.flush();\r
209     }\r
210 \r
211     /**\r
212      * Returns a list of items in the collection, with each separated by the separator.\r
213      * Each item must not be null; its toString() is called for a printable representation\r
214      * @param filename destination to which to write names\r
215      * @param c source collection\r
216      */\r
217     public void showSetNames(String filename, Object c) throws IOException {\r
218         PrintWriter pw = new PrintWriter(\r
219             new OutputStreamWriter(\r
220                 new FileOutputStream(filename),"utf-8"));\r
221         showSetNames(log,c);\r
222         pw.close();\r
223     }\r
224 \r
225     public String getAbbreviatedName(\r
226         String src,\r
227         String pattern,\r
228         String substitute) {\r
229 \r
230         int matchEnd = NameIterator.findMatchingEnd(src, pattern);\r
231         int sdiv = src.length() - matchEnd;\r
232         int pdiv = pattern.length() - matchEnd;\r
233         StringBuffer result = new StringBuffer();\r
234         addMatching(\r
235             src.substring(0, sdiv),\r
236             pattern.substring(0, pdiv),\r
237             substitute,\r
238             result);\r
239         addMatching(\r
240             src.substring(sdiv),\r
241             pattern.substring(pdiv),\r
242             substitute,\r
243             result);\r
244         return result.toString();\r
245     }\r
246 \r
247     abstract public static class Relation {\r
248         abstract public String getRelation(String a, String b);\r
249     }\r
250 \r
251     static class NullRelation extends Relation {\r
252         public String getRelation(String a, String b) { return ""; }\r
253     }\r
254 \r
255     private Relation r = new NullRelation();\r
256 \r
257     public BagFormatter setRelation(Relation r) {\r
258         this.r = r;\r
259         return this; // for chaining\r
260     }\r
261 \r
262     public Relation getRelation() {\r
263         return r;\r
264     }\r
265 \r
266     /*\r
267      r.getRelati on(last, s) + quote(s) + "\t#" + UnicodeSetFormatter.getResolvedName(s)\r
268     */\r
269     /*\r
270     static final UnicodeSet NO_NAME =\r
271         new UnicodeSet("[\\u0080\\u0081\\u0084\\u0099\\p{Cn}\\p{Co}]");\r
272     static final UnicodeSet HAS_NAME = new UnicodeSet(NO_NAME).complement();\r
273     static final UnicodeSet NAME_CHARACTERS =\r
274         new UnicodeSet("[A-Za-z0-9\\<\\>\\-\\ ]");\r
275 \r
276     public UnicodeSet getSetForName(String namePattern) {\r
277         UnicodeSet result = new UnicodeSet();\r
278         Matcher m = Pattern.compile(namePattern).matcher("");\r
279         // check for no-name items, and add in bulk\r
280         m.reset("<no name>");\r
281         if (m.matches()) {\r
282             result.addAll(NO_NAME);\r
283         }\r
284         // check all others\r
285         UnicodeSetIterator usi = new UnicodeSetIterator(HAS_NAME);\r
286         while (usi.next()) {\r
287             String name = getName(usi.codepoint);\r
288             if (name == null)\r
289                 continue;\r
290             m.reset(name);\r
291             if (m.matches()) {\r
292                 result.add(usi.codepoint);\r
293             }\r
294         }\r
295         // Note: if Regex had some API so that if we could tell that\r
296         // an initial substring couldn't match, e.g. "CJK IDEOGRAPH-"\r
297         // then we could optimize by skipping whole swathes of characters\r
298         return result;\r
299     }\r
300     */\r
301 \r
302     public BagFormatter setMergeRanges(boolean in) {\r
303         mergeRanges = in;\r
304         return this;\r
305     }\r
306     public BagFormatter setShowSetAlso(boolean b) {\r
307         showSetAlso = b;\r
308         return this;\r
309     }\r
310 \r
311     public String getName(int codePoint) {\r
312         return getName("", codePoint, codePoint);\r
313     }\r
314 \r
315     public String getName(String sep, int start, int end) {\r
316         if (getNameSource() == null || getNameSource() == UnicodeLabel.NULL) return "";\r
317         String result = getName(start, false);\r
318         if (start == end) return sep + result;\r
319         String endString = getName(end, false);\r
320         if (result.length() == 0 && endString.length() == 0) return sep;\r
321         if (abbreviated) endString = getAbbreviatedName(endString,result,"~");\r
322         return sep + result + ".." + endString;\r
323     }\r
324 \r
325     public String getName(String s) {\r
326         return getName(s, false);\r
327     }\r
328 \r
329     public static class NameLabel extends UnicodeLabel {\r
330         UnicodeProperty nameProp;\r
331         UnicodeSet control;\r
332         UnicodeSet private_use;\r
333         UnicodeSet noncharacter;\r
334         UnicodeSet surrogate;\r
335 \r
336         public NameLabel(UnicodeProperty.Factory source) {\r
337             nameProp = source.getProperty("Name");\r
338             control = source.getSet("gc=Cc");\r
339             private_use = source.getSet("gc=Co");\r
340             surrogate = source.getSet("gc=Cs");\r
341             noncharacter = source.getSet("noncharactercodepoint=yes");\r
342         }\r
343 \r
344         public String getValue(int codePoint, boolean isShort) {\r
345             String hcp = !isShort\r
346                 ? "U+" + Utility.hex(codePoint, 4) + " "\r
347                 : "";\r
348             String result = nameProp.getValue(codePoint);\r
349             if (result != null)\r
350                 return hcp + result;\r
351             if (control.contains(codePoint)) {\r
352                 return "<control-" + Utility.hex(codePoint, 4) + ">";\r
353             }\r
354             if (private_use.contains(codePoint)) {\r
355                 return "<private-use-" + Utility.hex(codePoint, 4) + ">";\r
356             }\r
357             if (surrogate.contains(codePoint)) {\r
358                 return "<surrogate-" + Utility.hex(codePoint, 4) + ">";\r
359             }\r
360             if (noncharacter.contains(codePoint)) {\r
361                 return "<noncharacter-" + Utility.hex(codePoint, 4) + ">";\r
362             }\r
363             //if (suppressReserved) return "";\r
364             return hcp + "<reserved-" + Utility.hex(codePoint, 4) + ">";\r
365         }\r
366 \r
367     }\r
368 \r
369     // refactored\r
370     public String getName(int codePoint, boolean withCodePoint) {\r
371         String result = getNameSource().getValue(codePoint, !withCodePoint);\r
372         return fixName == null ? result : fixName.transliterate(result);\r
373     }\r
374 \r
375     public String getName(String s, boolean withCodePoint) {\r
376            String result = getNameSource().getValue(s, separator, !withCodePoint);\r
377         return fixName == null ? result : fixName.transliterate(result);\r
378      }\r
379 \r
380     public String hex(String s) {\r
381         return hex(s,separator);\r
382     }\r
383 \r
384     public String hex(String s, String sep) {\r
385         return UnicodeLabel.HEX.getValue(s, sep, true);\r
386     }\r
387 \r
388     public String hex(int start, int end) {\r
389         String s = Utility.hex(start,4);\r
390         if (start == end) return s;\r
391         return s + ".." + Utility.hex(end,4);\r
392     }\r
393 \r
394     public BagFormatter setUnicodePropertyFactory(UnicodeProperty.Factory source) {\r
395         this.source = source;\r
396         return this;\r
397     }\r
398 \r
399     public UnicodeProperty.Factory getUnicodePropertyFactory() {\r
400         if (source == null) source = ICUPropertyFactory.make();\r
401         return source;\r
402     }\r
403 \r
404     public BagFormatter () {\r
405     }\r
406 \r
407     public BagFormatter (UnicodeProperty.Factory source) {\r
408         setUnicodePropertyFactory(source);\r
409     }\r
410 \r
411     public String join(Object o) {\r
412         return labelVisitor.join(o);\r
413     }\r
414 \r
415     // ===== PRIVATES =====\r
416 \r
417     private Join labelVisitor = new Join();\r
418 \r
419     private boolean mergeRanges = true;\r
420     private Transliterator showLiteral = null;\r
421     private Transliterator fixName = null;\r
422     private boolean showSetAlso = false;\r
423 \r
424     private RangeFinder rf = new RangeFinder();\r
425 \r
426     private MessageFormat inOut = new MessageFormat("In {0}, but not in {1}:");\r
427     private MessageFormat outIn = new MessageFormat("Not in {0}, but in {1}:");\r
428     private MessageFormat inIn = new MessageFormat("In both {0}, and in {1}:");\r
429 \r
430     private MyVisitor mainVisitor = new MyVisitor();\r
431 \r
432     /*\r
433     private String getLabels(int start, int end) {\r
434         Set names = new TreeSet();\r
435         for (int cp = start; cp <= end; ++cp) {\r
436             names.add(getLabel(cp));\r
437         }\r
438         return labelVisitor.join(names);\r
439     }\r
440     */\r
441 \r
442     private void addMatching(\r
443         String src,\r
444         String pattern,\r
445         String substitute,\r
446         StringBuffer result) {\r
447         NameIterator n1 = new NameIterator(src);\r
448         NameIterator n2 = new NameIterator(pattern);\r
449         boolean first = true;\r
450         while (true) {\r
451             String s1 = n1.next();\r
452             if (s1 == null)\r
453                 break;\r
454             String s2 = n2.next();\r
455             if (!first)\r
456                 result.append(" ");\r
457             first = false;\r
458             if (s1.equals(s2))\r
459                 result.append(substitute);\r
460             else\r
461                 result.append(s1);\r
462         }\r
463     }\r
464 \r
465     private static NumberFormat nf =\r
466         NumberFormat.getIntegerInstance(Locale.ENGLISH);\r
467     static {\r
468         nf.setGroupingUsed(false);\r
469     }\r
470 \r
471     private class MyVisitor extends Visitor {\r
472         private PrintWriter output;\r
473         String commentSeparator;\r
474         int counter;\r
475         int valueSize;\r
476         int labelSize;\r
477         boolean isHtml;\r
478         boolean inTable = false;\r
479         \r
480         public void toOutput(String s) {\r
481           if (isHtml) {\r
482             if (inTable) {\r
483               output.print("</table>");\r
484               inTable = false;\r
485             }\r
486             output.print("<p>");\r
487           }\r
488           output.print(s);\r
489           if (isHtml)\r
490             output.println("</p>");\r
491           else\r
492             output.print(lineSeparator);\r
493         }\r
494         \r
495         public void toTable(String s) {\r
496           if (isHtml && !inTable) {\r
497             output.print("<table>");\r
498             inTable = true;\r
499           }\r
500           output.print(tabber.process(s) +  lineSeparator);\r
501         }\r
502 \r
503         public void doAt(Object c, PrintWriter out) {\r
504             output = out;\r
505             isHtml = tabber instanceof Tabber.HTMLTabber;\r
506             counter = 0;\r
507             \r
508             tabber.clear();\r
509             // old:\r
510             // 0009..000D    ; White_Space # Cc   [5] <control-0009>..<control-000D>\r
511             // new\r
512             // 0009..000D    ; White_Space #Cc  [5] <control>..<control>\r
513             tabber.add(mergeRanges ? 14 : 6,Tabber.LEFT);\r
514 \r
515             if (propName.length() > 0) {\r
516                 tabber.add(propName.length() + 2,Tabber.LEFT);\r
517             }\r
518 \r
519             valueSize = getValueSource().getMaxWidth(shortValue);\r
520             if (DEBUG) System.out.println("ValueSize: " + valueSize);\r
521             if (valueSize > 0) {\r
522                 tabber.add(valueSize + 2,Tabber.LEFT); // value\r
523             }\r
524 \r
525             tabber.add(3,Tabber.LEFT); // comment character\r
526 \r
527             labelSize = getLabelSource(true).getMaxWidth(shortLabel);\r
528             if (labelSize > 0) {\r
529                 tabber.add(labelSize + 1,Tabber.LEFT); // value\r
530             }\r
531 \r
532             if (mergeRanges && showCount) {\r
533                 tabber.add(5,Tabber.RIGHT);\r
534             }\r
535 \r
536             if (showLiteral != null) {\r
537                 tabber.add(4,Tabber.LEFT);\r
538             }\r
539             //myTabber.add(7,Tabber.LEFT);\r
540 \r
541             commentSeparator = (showCount || showLiteral != null\r
542               || getLabelSource(true) != UnicodeLabel.NULL\r
543               || getNameSource() != UnicodeLabel.NULL)\r
544             ? "\t #" : "";\r
545 \r
546             if (DEBUG) System.out.println("Tabber: " + tabber.toString());\r
547             if (DEBUG) System.out.println("Tabber: " + tabber.process(\r
548                     "200C..200D\t; White_Space\t #\tCf\t [2]\t ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER"));\r
549             doAt(c);\r
550         }\r
551 \r
552         @SuppressWarnings("unused")\r
553         public String format(Object o) {\r
554             StringWriter sw = new StringWriter();\r
555             PrintWriter pw = new PrintWriter(sw);\r
556             doAt(o);\r
557             pw.flush();\r
558             String result = sw.getBuffer().toString();\r
559             pw.close();\r
560             return result;\r
561         }\r
562 \r
563         protected void doBefore(Object container, Object o) {\r
564             if (showSetAlso && container instanceof UnicodeSet) {\r
565               toOutput("#" + container);\r
566             }\r
567         }\r
568 \r
569         protected void doBetween(Object container, Object lastItem, Object nextItem) {\r
570         }\r
571 \r
572         protected void doAfter(Object container, Object o) {\r
573             if (fullTotal != -1 && fullTotal != counter) {\r
574                 if (showTotal) {\r
575                     toOutput("");\r
576                     toOutput("# The above property value applies to " + nf.format(fullTotal-counter) + " code points not listed here.");\r
577                     toOutput("# Total code points: " + nf.format(fullTotal));\r
578                 }\r
579                 fullTotal = -1;\r
580             } else if (showTotal) {\r
581                 toOutput("");\r
582                 toOutput("# Total code points: " + nf.format(counter));\r
583             }\r
584         }\r
585 \r
586         protected void doSimpleAt(Object o) {\r
587             if (o instanceof Map.Entry) {\r
588                 Map.Entry oo = (Map.Entry)o;\r
589                 Object key = oo.getKey();\r
590                 Object value = oo.getValue();\r
591                 doBefore(o, key);\r
592                 doAt(key);\r
593                 output.println("\u2192");\r
594                 doAt(value);\r
595                 doAfter(o, value);\r
596                 counter++;\r
597             } else if (o instanceof Visitor.CodePointRange) {\r
598                 doAt((Visitor.CodePointRange) o);\r
599             } else {\r
600                 String thing = o.toString();\r
601                 String value = getValueSource() == UnicodeLabel.NULL ? "" : getValueSource().getValue(thing, ",", true);\r
602                 if (getValueSource() != UnicodeLabel.NULL) value = "\t; " + value;\r
603                 String label = getLabelSource(true) == UnicodeLabel.NULL ? "" : getLabelSource(true).getValue(thing, ",", true);\r
604                 if (label.length() != 0) label = " " + label;\r
605                 toTable(\r
606                     hex(thing)\r
607                     + value\r
608                     + commentSeparator\r
609                     + label\r
610                     + insertLiteral(thing)\r
611                     + "\t"\r
612                     + getName(thing));\r
613                 counter++;\r
614             }\r
615         }\r
616 \r
617         protected void doAt(Visitor.CodePointRange usi) {\r
618             if (!mergeRanges) {\r
619                 for (int cp = usi.codepoint; cp <= usi.codepointEnd; ++cp) {\r
620                     showLine(cp, cp);\r
621                 }\r
622             } else {\r
623                 rf.reset(usi.codepoint, usi.codepointEnd + 1);\r
624                 while (rf.next()) {\r
625                     showLine(rf.start, rf.limit - 1);\r
626                 }\r
627             }\r
628         }\r
629 \r
630         private void showLine(int start, int end) {\r
631             String label = getLabelSource(true).getValue(start, shortLabel);\r
632             String value = getValue(start, shortValue);\r
633             if (value == NULL_VALUE) return;\r
634 \r
635             counter += end - start + 1;\r
636             String pn = propName;\r
637             if (pn.length() != 0) {\r
638                 pn = "\t; " + pn;\r
639             }\r
640             if (valueSize > 0) {\r
641                 value = "\t; " + value;\r
642             } else if (value.length() > 0) {\r
643                 throw new IllegalArgumentException("maxwidth bogus " + value + "," + getValueSource().getMaxWidth(shortValue));\r
644             }\r
645             if (labelSize > 0) {\r
646                 label = "\t" + label;\r
647             } else if (label.length() > 0) {\r
648                 throw new IllegalArgumentException("maxwidth bogus " + label + ", " + getLabelSource(true).getMaxWidth(shortLabel));\r
649             }\r
650 \r
651             String count = "";\r
652             if (mergeRanges && showCount) {\r
653                 if (end == start) count = "\t";\r
654                 else count = "\t ["+ nf.format(end - start + 1)+ "]";\r
655            }\r
656 \r
657             toTable(\r
658                 hex(start, end)\r
659                 + pn\r
660                 + value\r
661                 + commentSeparator\r
662                 + label\r
663                 + count\r
664                 + insertLiteral(start, end)\r
665                 + getName("\t ", start, end));\r
666         }\r
667 \r
668         private String insertLiteral(String thing) {\r
669             return (showLiteral == null ? ""\r
670                 :  " \t(" + showLiteral.transliterate(thing) + ") ");\r
671         }\r
672 \r
673         private String insertLiteral(int start, int end) {\r
674             return (showLiteral == null ? "" :\r
675                 " \t(" + showLiteral.transliterate(UTF16.valueOf(start))\r
676                         + ((start != end)\r
677                             ? (".." + showLiteral.transliterate(UTF16.valueOf(end)))\r
678                             : "")\r
679                 + ") ");\r
680         }\r
681         /*\r
682         private String insertLiteral(int cp) {\r
683             return (showLiteral == null ? ""\r
684                 :  " \t(" + showLiteral.transliterate(UTF16.valueOf(cp)) + ") ");\r
685         }\r
686         */\r
687     }\r
688 \r
689     /**\r
690      * Iterate through a string, breaking at words.\r
691      * @author Davis\r
692      */\r
693     private static class NameIterator {\r
694         String source;\r
695         int position;\r
696         int limit;\r
697 \r
698         NameIterator(String source) {\r
699             this.source = source;\r
700             this.limit = source.length();\r
701         }\r
702         /**\r
703          * Find next word, including trailing spaces\r
704          * @return the next word\r
705          */\r
706         String next() {\r
707             if (position >= limit)\r
708                 return null;\r
709             int pos = source.indexOf(' ', position);\r
710             if (pos < 0 || pos >= limit)\r
711                 pos = limit;\r
712             String result = source.substring(position, pos);\r
713             position = pos + 1;\r
714             return result;\r
715         }\r
716 \r
717         static int findMatchingEnd(String s1, String s2) {\r
718             int i = s1.length();\r
719             int j = s2.length();\r
720             try {\r
721                 while (true) {\r
722                     --i; // decrement both before calling function!\r
723                     --j;\r
724                     if (s1.charAt(i) != s2.charAt(j))\r
725                         break;\r
726                 }\r
727             } catch (Exception e) {} // run off start\r
728 \r
729             ++i; // counteract increment\r
730             i = s1.indexOf(' ', i); // move forward to space\r
731             if (i < 0)\r
732                 return 0;\r
733             return s1.length() - i;\r
734         }\r
735     }\r
736 \r
737     private class RangeFinder {\r
738         int start, limit;\r
739         private int veryLimit;\r
740         //String label, value;\r
741         void reset(int rangeStart, int rangeLimit) {\r
742             limit = rangeStart;\r
743             veryLimit = rangeLimit;\r
744         }\r
745         boolean next() {\r
746             if (limit >= veryLimit)\r
747                 return false;\r
748             start = limit; // set to end of last\r
749             String label = getLabelSource(false).getValue(limit, true);\r
750             String value = getValue(limit, true);\r
751             String breaker = getRangeBreakSource().getValue(limit,true);\r
752             if (DEBUG && limit < 0x7F) System.out.println("Label: " + label + ", Value: " + value + ", Break: " + breaker);\r
753             limit++;\r
754             for (; limit < veryLimit; limit++) {\r
755                 String s = getLabelSource(false).getValue(limit, true);\r
756                 String v = getValue(limit, true);\r
757                 String b = getRangeBreakSource().getValue(limit, true);\r
758                 if (DEBUG && limit < 0x7F) System.out.println("*Label: " + label + ", Value: " + value + ", Break: " + breaker);\r
759                 if (!equalTo(s, label) || !equalTo(v, value) || !equalTo(b, breaker)) break;\r
760             }\r
761             // at this point, limit is the first item that has a different label than source\r
762             // OR, we got to the end, and limit == veryLimit\r
763             return true;\r
764         }\r
765     }\r
766 \r
767     boolean equalTo(Object a, Object b) {\r
768         if (a == b) return true;\r
769         if (a == null) return false;\r
770         return a.equals(b);\r
771     }\r
772 \r
773     boolean shortLabel = true;\r
774     boolean shortValue = true;\r
775 \r
776     public String getPrefix() {\r
777         return prefix;\r
778     }\r
779 \r
780     public String getSuffix() {\r
781         return suffix;\r
782     }\r
783 \r
784     public BagFormatter setPrefix(String string) {\r
785         prefix = string;\r
786         return this;\r
787     }\r
788 \r
789     public BagFormatter setSuffix(String string) {\r
790         suffix = string;\r
791         return this;\r
792     }\r
793 \r
794     public boolean isAbbreviated() {\r
795         return abbreviated;\r
796     }\r
797 \r
798     public BagFormatter setAbbreviated(boolean b) {\r
799         abbreviated = b;\r
800         return this;\r
801     }\r
802 \r
803     public UnicodeLabel getLabelSource(boolean visible) {\r
804         if (labelSource == null) {\r
805             Map labelMap = new HashMap();\r
806             //labelMap.put("Lo","L&");\r
807             labelMap.put("Lu","L&");\r
808             labelMap.put("Lt","L&");\r
809             labelMap.put("Ll","L&");\r
810             labelSource = new UnicodeProperty.FilteredProperty(\r
811                 getUnicodePropertyFactory().getProperty("General_Category"),\r
812                 new UnicodeProperty.MapFilter(labelMap)\r
813             ).setAllowValueAliasCollisions(true);\r
814         }\r
815         return labelSource;\r
816     }\r
817 \r
818     /**\r
819      * @deprecated\r
820      */\r
821     public static void addAll(UnicodeSet source, Collection target) {\r
822         source.addAllTo(target);\r
823     }\r
824 \r
825     // UTILITIES\r
826 \r
827     public static final Transliterator hex = Transliterator.getInstance(\r
828         "[^\\u0009\\u0020-\\u007E\\u00A0-\\u00FF] hex");\r
829 \r
830     public static BufferedReader openUTF8Reader(String dir, String filename) throws IOException {\r
831         return openReader(dir,filename,"UTF-8");\r
832     }\r
833 \r
834     public static BufferedReader openReader(String dir, String filename, String encoding) throws IOException {\r
835         File file = dir.length() == 0 ? new File(filename) : new File(dir, filename);\r
836         if (SHOW_FILES && log != null) {\r
837             log.println("Opening File: "\r
838                 + file.getCanonicalPath());\r
839         }\r
840         return new BufferedReader(\r
841             new InputStreamReader(\r
842                 new FileInputStream(file),\r
843                 encoding),\r
844             4*1024);\r
845     }\r
846 \r
847     public static PrintWriter openUTF8Writer(String dir, String filename) throws IOException {\r
848         return openWriter(dir,filename,"UTF-8");\r
849     }\r
850 \r
851     public static PrintWriter openWriter(String dir, String filename, String encoding) throws IOException {\r
852         File file = new File(dir, filename);\r
853         if (SHOW_FILES && log != null) {\r
854             log.println("Creating File: "\r
855                 + file.getCanonicalPath());\r
856         }\r
857         String parentName = file.getParent();\r
858         if (parentName != null) {\r
859             File parent = new File(parentName);\r
860             parent.mkdirs();\r
861         }\r
862         return new PrintWriter(\r
863             new BufferedWriter(\r
864                 new OutputStreamWriter(\r
865                     new FileOutputStream(file),\r
866                     encoding),\r
867                 4*1024));\r
868     }\r
869     public static PrintWriter getLog() {\r
870         return log;\r
871     }\r
872     public BagFormatter setLog(PrintWriter writer) {\r
873         log = writer;\r
874         return this;\r
875     }\r
876     public String getSeparator() {\r
877         return separator;\r
878     }\r
879     public BagFormatter setSeparator(String string) {\r
880         separator = string;\r
881         return this;\r
882     }\r
883     public Transliterator getShowLiteral() {\r
884         return showLiteral;\r
885     }\r
886     public BagFormatter setShowLiteral(Transliterator transliterator) {\r
887         showLiteral = transliterator;\r
888         return this;\r
889     }\r
890 \r
891     // ===== CONVENIENCES =====\r
892     private class Join extends Visitor {\r
893         StringBuffer output = new StringBuffer();\r
894         int depth = 0;\r
895         String join (Object o) {\r
896             output.setLength(0);\r
897             doAt(o);\r
898             return output.toString();\r
899         }\r
900         protected void doBefore(Object container, Object item) {\r
901             ++depth;\r
902             output.append(prefix);\r
903         }\r
904         protected void doAfter(Object container, Object item) {\r
905             output.append(suffix);\r
906             --depth;\r
907         }\r
908         protected void doBetween(Object container, Object lastItem, Object nextItem) {\r
909             output.append(separator);\r
910         }\r
911         protected void doSimpleAt(Object o) {\r
912             if (o != null) output.append(o.toString());\r
913         }\r
914     }\r
915 \r
916     /**\r
917      * @param label\r
918      */\r
919     public BagFormatter setLabelSource(UnicodeLabel label) {\r
920         if (label == null) label = UnicodeLabel.NULL;\r
921         labelSource = label;\r
922         return this;\r
923     }\r
924 \r
925     /**\r
926      * @return the NameLable representing the source\r
927      */\r
928     public UnicodeLabel getNameSource() {\r
929         if (nameSource == null) {\r
930             nameSource = new NameLabel(getUnicodePropertyFactory());\r
931         }\r
932         return nameSource;\r
933     }\r
934 \r
935     /**\r
936      * @param label\r
937      */\r
938     public BagFormatter setNameSource(UnicodeLabel label) {\r
939         if (label == null) label = UnicodeLabel.NULL;\r
940         nameSource = label;\r
941         return this;\r
942     }\r
943 \r
944     /**\r
945      * @return the UnicodeLabel representing the value\r
946      */\r
947     public UnicodeLabel getValueSource() {\r
948         if (valueSource == null) valueSource = UnicodeLabel.NULL;\r
949         return valueSource;\r
950     }\r
951 \r
952     private String getValue(int cp, boolean shortVal) {\r
953         String result = getValueSource().getValue(cp, shortVal);\r
954         if (result == null) return NULL_VALUE;\r
955         if (hexValue) result = hex(result, " ");\r
956         return result;\r
957     }\r
958 \r
959     /**\r
960      * @param label\r
961      */\r
962     public BagFormatter setValueSource(UnicodeLabel label) {\r
963         if (label == null) label = UnicodeLabel.NULL;\r
964         valueSource = label;\r
965         return this;\r
966     }\r
967 \r
968     public BagFormatter setValueSource(String label) {\r
969         return setValueSource(new UnicodeLabel.Constant(label));\r
970     }\r
971 \r
972     /**\r
973      * @return true if showCount is true\r
974      */\r
975     public boolean isShowCount() {\r
976         return showCount;\r
977     }\r
978 \r
979     /**\r
980      * @param b true to show the count\r
981      * @return this (for chaining)\r
982      */\r
983     public BagFormatter setShowCount(boolean b) {\r
984         showCount = b;\r
985         return this;\r
986     }\r
987 \r
988     /**\r
989      * @return the property name\r
990      */\r
991     public String getPropName() {\r
992         return propName;\r
993     }\r
994 \r
995     /**\r
996      * @param string\r
997      * @return this (for chaining)\r
998      */\r
999     public BagFormatter setPropName(String string) {\r
1000         if (string == null) string = "";\r
1001         propName = string;\r
1002         return this;\r
1003     }\r
1004 \r
1005     /**\r
1006      * @return true if this is a hexValue\r
1007      */\r
1008     public boolean isHexValue() {\r
1009         return hexValue;\r
1010     }\r
1011 \r
1012     /**\r
1013      * @param b\r
1014      * @return this (for chaining)\r
1015      */\r
1016     public BagFormatter setHexValue(boolean b) {\r
1017         hexValue = b;\r
1018         return this;\r
1019     }\r
1020 \r
1021     /**\r
1022      * @return the full total\r
1023      */\r
1024     public int getFullTotal() {\r
1025         return fullTotal;\r
1026     }\r
1027 \r
1028     /**\r
1029      * @param i set the full total\r
1030      * @return this (for chaining)\r
1031      */\r
1032     public BagFormatter setFullTotal(int i) {\r
1033         fullTotal = i;\r
1034         return this;\r
1035     }\r
1036 \r
1037     /**\r
1038      * @return the line separator\r
1039      */\r
1040     public String getLineSeparator() {\r
1041         return lineSeparator;\r
1042     }\r
1043 \r
1044     /**\r
1045      * @param string\r
1046      * @return this (for chaining)\r
1047      */\r
1048     public BagFormatter setLineSeparator(String string) {\r
1049         lineSeparator = string;\r
1050         return this;\r
1051     }\r
1052 \r
1053     /**\r
1054      * @return the UnicodeLabel representing the range break source\r
1055      */\r
1056     public UnicodeLabel getRangeBreakSource() {\r
1057         if (rangeBreakSource == null) {\r
1058             Map labelMap = new HashMap();\r
1059             // reflects the code point types on p 25\r
1060             labelMap.put("Lo", "G&");\r
1061             labelMap.put("Lm", "G&");\r
1062             labelMap.put("Lu", "G&");\r
1063             labelMap.put("Lt", "G&");\r
1064             labelMap.put("Ll", "G&");\r
1065             labelMap.put("Mn", "G&");\r
1066             labelMap.put("Me", "G&");\r
1067             labelMap.put("Mc", "G&");\r
1068             labelMap.put("Nd", "G&");\r
1069             labelMap.put("Nl", "G&");\r
1070             labelMap.put("No", "G&");\r
1071             labelMap.put("Zs", "G&");\r
1072             labelMap.put("Pd", "G&");\r
1073             labelMap.put("Ps", "G&");\r
1074             labelMap.put("Pe", "G&");\r
1075             labelMap.put("Pc", "G&");\r
1076             labelMap.put("Po", "G&");\r
1077             labelMap.put("Pi", "G&");\r
1078             labelMap.put("Pf", "G&");\r
1079             labelMap.put("Sm", "G&");\r
1080             labelMap.put("Sc", "G&");\r
1081             labelMap.put("Sk", "G&");\r
1082             labelMap.put("So", "G&");\r
1083 \r
1084             labelMap.put("Zl", "Cf");\r
1085             labelMap.put("Zp", "Cf");\r
1086 \r
1087             rangeBreakSource =\r
1088                 new UnicodeProperty\r
1089                     .FilteredProperty(\r
1090                         getUnicodePropertyFactory().getProperty(\r
1091                             "General_Category"),\r
1092                         new UnicodeProperty.MapFilter(labelMap))\r
1093                     .setAllowValueAliasCollisions(true);\r
1094 \r
1095             /*\r
1096             "Cn", // = Other, Not Assigned 0\r
1097             "Cc", // = Other, Control 15\r
1098             "Cf", // = Other, Format 16\r
1099             UnicodeProperty.UNUSED, // missing\r
1100             "Co", // = Other, Private Use 18\r
1101             "Cs", // = Other, Surrogate 19\r
1102             */\r
1103         }\r
1104         return rangeBreakSource;\r
1105     }\r
1106 \r
1107     /**\r
1108      * @param label\r
1109      */\r
1110     public BagFormatter setRangeBreakSource(UnicodeLabel label) {\r
1111         if (label == null) label = UnicodeLabel.NULL;\r
1112         rangeBreakSource = label;\r
1113         return this;\r
1114     }\r
1115 \r
1116     /**\r
1117      * @return Returns the fixName.\r
1118      */\r
1119     public Transliterator getFixName() {\r
1120         return fixName;\r
1121     }\r
1122     /**\r
1123      * @param fixName The fixName to set.\r
1124      */\r
1125     public BagFormatter setFixName(Transliterator fixName) {\r
1126         this.fixName = fixName;\r
1127         return this;\r
1128     }\r
1129 \r
1130     public Tabber getTabber() {\r
1131         return tabber;\r
1132     }\r
1133 \r
1134     public void setTabber(Tabber tabber) {\r
1135         this.tabber = tabber;\r
1136     }\r
1137 \r
1138     public boolean isShowTotal() {\r
1139         return showTotal;\r
1140     }\r
1141 \r
1142     public void setShowTotal(boolean showTotal) {\r
1143         this.showTotal = showTotal;\r
1144     }\r
1145 }\r