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