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