| /* |
| ******************************************************************************* |
| * Copyright (C) 2002-2012, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.dev.util; |
| |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.text.MessageFormat; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import com.ibm.icu.impl.Utility; |
| import com.ibm.icu.text.NumberFormat; |
| import com.ibm.icu.text.Transliterator; |
| import com.ibm.icu.text.UTF16; |
| import com.ibm.icu.text.UnicodeSet; |
| |
| public class BagFormatter { |
| static final boolean DEBUG = false; |
| public static final boolean SHOW_FILES; |
| static { |
| boolean showFiles = false; |
| try { |
| showFiles = System.getProperty("SHOW_FILES") != null; |
| } |
| catch (SecurityException e) { |
| } |
| SHOW_FILES = showFiles; |
| } |
| |
| public static final PrintWriter CONSOLE = new PrintWriter(System.out,true); |
| |
| private static PrintWriter log = CONSOLE; |
| |
| private boolean abbreviated = false; |
| private String separator = ","; |
| private String prefix = "["; |
| private String suffix = "]"; |
| private UnicodeProperty.Factory source; |
| private UnicodeLabel nameSource; |
| private UnicodeLabel labelSource; |
| private UnicodeLabel rangeBreakSource; |
| private UnicodeLabel valueSource; |
| private String propName = ""; |
| private boolean showCount = true; |
| //private boolean suppressReserved = true; |
| private boolean hexValue = false; |
| private static final String NULL_VALUE = "_NULL_VALUE_"; |
| private int fullTotal = -1; |
| private boolean showTotal = true; |
| private String lineSeparator = "\r\n"; |
| private Tabber tabber = new Tabber.MonoTabber(); |
| |
| /** |
| * Compare two UnicodeSets, and show the differences |
| * @param name1 name of first set to be compared |
| * @param set1 first set |
| * @param name2 name of second set to be compared |
| * @param set2 second set |
| * @return formatted string |
| */ |
| public String showSetDifferences( |
| String name1, |
| UnicodeSet set1, |
| String name2, |
| UnicodeSet set2) { |
| |
| StringWriter result = new StringWriter(); |
| showSetDifferences(new PrintWriter(result),name1,set1,name2,set2); |
| result.flush(); |
| return result.getBuffer().toString(); |
| } |
| |
| public String showSetDifferences( |
| String name1, |
| Collection set1, |
| String name2, |
| Collection set2) { |
| |
| StringWriter result = new StringWriter(); |
| showSetDifferences(new PrintWriter(result), name1, set1, name2, set2); |
| result.flush(); |
| return result.getBuffer().toString(); |
| } |
| |
| public void showSetDifferences( |
| PrintWriter pw, |
| String name1, |
| UnicodeSet set1, |
| String name2, |
| UnicodeSet set2) { |
| showSetDifferences(pw, name1, set1, name2, set2, -1); |
| } |
| /** |
| * Compare two UnicodeSets, and show the differences |
| * @param name1 name of first set to be compared |
| * @param set1 first set |
| * @param name2 name of second set to be compared |
| * @param set2 second set |
| */ |
| public void showSetDifferences( |
| PrintWriter pw, |
| String name1, |
| UnicodeSet set1, |
| String name2, |
| UnicodeSet set2, |
| int flags) |
| { |
| if (pw == null) pw = CONSOLE; |
| String[] names = { name1, name2 }; |
| |
| UnicodeSet temp; |
| |
| if ((flags&1) != 0) { |
| temp = new UnicodeSet(set1).removeAll(set2); |
| pw.print(lineSeparator); |
| pw.print(inOut.format(names)); |
| pw.print(lineSeparator); |
| showSetNames(pw, temp); |
| } |
| |
| if ((flags&2) != 0) { |
| temp = new UnicodeSet(set2).removeAll(set1); |
| pw.print(lineSeparator); |
| pw.print(outIn.format(names)); |
| pw.print(lineSeparator); |
| showSetNames(pw, temp); |
| } |
| |
| if ((flags&4) != 0) { |
| temp = new UnicodeSet(set2).retainAll(set1); |
| pw.print(lineSeparator); |
| pw.print(inIn.format(names)); |
| pw.print(lineSeparator); |
| showSetNames(pw, temp); |
| } |
| pw.flush(); |
| } |
| |
| public void showSetDifferences( |
| PrintWriter pw, |
| String name1, |
| Collection set1, |
| String name2, |
| Collection set2) { |
| |
| if (pw == null) pw = CONSOLE; |
| String[] names = { name1, name2 }; |
| // damn'd collection doesn't have a clone, so |
| // we go with Set, even though that |
| // may not preserve order and duplicates |
| Collection temp = new HashSet(set1); |
| temp.removeAll(set2); |
| pw.println(); |
| pw.println(inOut.format(names)); |
| showSetNames(pw, temp); |
| |
| temp.clear(); |
| temp.addAll(set2); |
| temp.removeAll(set1); |
| pw.println(); |
| pw.println(outIn.format(names)); |
| showSetNames(pw, temp); |
| |
| temp.clear(); |
| temp.addAll(set1); |
| temp.retainAll(set2); |
| pw.println(); |
| pw.println(inIn.format(names)); |
| showSetNames(pw, temp); |
| } |
| |
| /** |
| * Returns a list of items in the collection, with each separated by the separator. |
| * Each item must not be null; its toString() is called for a printable representation |
| * @param c source collection |
| * @return a String representation of the list |
| */ |
| public String showSetNames(Object c) { |
| StringWriter buffer = new StringWriter(); |
| PrintWriter output = new PrintWriter(buffer); |
| showSetNames(output,c); |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns a list of items in the collection, with each separated by the separator. |
| * Each item must not be null; its toString() is called for a printable representation |
| * @param output destination to which to write names |
| * @param c source collection |
| */ |
| public void showSetNames(PrintWriter output, Object c) { |
| mainVisitor.doAt(c, output); |
| output.flush(); |
| } |
| |
| /** |
| * Returns a list of items in the collection, with each separated by the separator. |
| * Each item must not be null; its toString() is called for a printable representation |
| * @param filename destination to which to write names |
| * @param c source collection |
| */ |
| public void showSetNames(String filename, Object c) throws IOException { |
| PrintWriter pw = new PrintWriter( |
| new OutputStreamWriter( |
| new FileOutputStream(filename),"utf-8")); |
| showSetNames(log,c); |
| pw.close(); |
| } |
| |
| public String getAbbreviatedName( |
| String src, |
| String pattern, |
| String substitute) { |
| |
| int matchEnd = NameIterator.findMatchingEnd(src, pattern); |
| int sdiv = src.length() - matchEnd; |
| int pdiv = pattern.length() - matchEnd; |
| StringBuffer result = new StringBuffer(); |
| addMatching( |
| src.substring(0, sdiv), |
| pattern.substring(0, pdiv), |
| substitute, |
| result); |
| addMatching( |
| src.substring(sdiv), |
| pattern.substring(pdiv), |
| substitute, |
| result); |
| return result.toString(); |
| } |
| |
| abstract public static class Relation { |
| abstract public String getRelation(String a, String b); |
| } |
| |
| static class NullRelation extends Relation { |
| public String getRelation(String a, String b) { return ""; } |
| } |
| |
| private Relation r = new NullRelation(); |
| |
| public BagFormatter setRelation(Relation r) { |
| this.r = r; |
| return this; // for chaining |
| } |
| |
| public Relation getRelation() { |
| return r; |
| } |
| |
| /* |
| r.getRelati on(last, s) + quote(s) + "\t#" + UnicodeSetFormatter.getResolvedName(s) |
| */ |
| /* |
| static final UnicodeSet NO_NAME = |
| new UnicodeSet("[\\u0080\\u0081\\u0084\\u0099\\p{Cn}\\p{Co}]"); |
| static final UnicodeSet HAS_NAME = new UnicodeSet(NO_NAME).complement(); |
| static final UnicodeSet NAME_CHARACTERS = |
| new UnicodeSet("[A-Za-z0-9\\<\\>\\-\\ ]"); |
| |
| public UnicodeSet getSetForName(String namePattern) { |
| UnicodeSet result = new UnicodeSet(); |
| Matcher m = Pattern.compile(namePattern).matcher(""); |
| // check for no-name items, and add in bulk |
| m.reset("<no name>"); |
| if (m.matches()) { |
| result.addAll(NO_NAME); |
| } |
| // check all others |
| UnicodeSetIterator usi = new UnicodeSetIterator(HAS_NAME); |
| while (usi.next()) { |
| String name = getName(usi.codepoint); |
| if (name == null) |
| continue; |
| m.reset(name); |
| if (m.matches()) { |
| result.add(usi.codepoint); |
| } |
| } |
| // Note: if Regex had some API so that if we could tell that |
| // an initial substring couldn't match, e.g. "CJK IDEOGRAPH-" |
| // then we could optimize by skipping whole swathes of characters |
| return result; |
| } |
| */ |
| |
| public BagFormatter setMergeRanges(boolean in) { |
| mergeRanges = in; |
| return this; |
| } |
| public BagFormatter setShowSetAlso(boolean b) { |
| showSetAlso = b; |
| return this; |
| } |
| |
| public String getName(int codePoint) { |
| return getName("", codePoint, codePoint); |
| } |
| |
| public String getName(String sep, int start, int end) { |
| if (getNameSource() == null || getNameSource() == UnicodeLabel.NULL) return ""; |
| String result = getName(start, false); |
| if (start == end) return sep + result; |
| String endString = getName(end, false); |
| if (result.length() == 0 && endString.length() == 0) return sep; |
| if (abbreviated) endString = getAbbreviatedName(endString,result,"~"); |
| return sep + result + ".." + endString; |
| } |
| |
| public String getName(String s) { |
| return getName(s, false); |
| } |
| |
| public static class NameLabel extends UnicodeLabel { |
| UnicodeProperty nameProp; |
| UnicodeSet control; |
| UnicodeSet private_use; |
| UnicodeSet noncharacter; |
| UnicodeSet surrogate; |
| |
| public NameLabel(UnicodeProperty.Factory source) { |
| nameProp = source.getProperty("Name"); |
| control = source.getSet("gc=Cc"); |
| private_use = source.getSet("gc=Co"); |
| surrogate = source.getSet("gc=Cs"); |
| noncharacter = source.getSet("noncharactercodepoint=yes"); |
| } |
| |
| public String getValue(int codePoint, boolean isShort) { |
| String hcp = !isShort |
| ? "U+" + Utility.hex(codePoint, 4) + " " |
| : ""; |
| String result = nameProp.getValue(codePoint); |
| if (result != null) |
| return hcp + result; |
| if (control.contains(codePoint)) { |
| return "<control-" + Utility.hex(codePoint, 4) + ">"; |
| } |
| if (private_use.contains(codePoint)) { |
| return "<private-use-" + Utility.hex(codePoint, 4) + ">"; |
| } |
| if (surrogate.contains(codePoint)) { |
| return "<surrogate-" + Utility.hex(codePoint, 4) + ">"; |
| } |
| if (noncharacter.contains(codePoint)) { |
| return "<noncharacter-" + Utility.hex(codePoint, 4) + ">"; |
| } |
| //if (suppressReserved) return ""; |
| return hcp + "<reserved-" + Utility.hex(codePoint, 4) + ">"; |
| } |
| |
| } |
| |
| // refactored |
| public String getName(int codePoint, boolean withCodePoint) { |
| String result = getNameSource().getValue(codePoint, !withCodePoint); |
| return fixName == null ? result : fixName.transliterate(result); |
| } |
| |
| public String getName(String s, boolean withCodePoint) { |
| String result = getNameSource().getValue(s, separator, !withCodePoint); |
| return fixName == null ? result : fixName.transliterate(result); |
| } |
| |
| public String hex(String s) { |
| return hex(s,separator); |
| } |
| |
| public String hex(String s, String sep) { |
| return UnicodeLabel.HEX.getValue(s, sep, true); |
| } |
| |
| public String hex(int start, int end) { |
| String s = Utility.hex(start,4); |
| if (start == end) return s; |
| return s + ".." + Utility.hex(end,4); |
| } |
| |
| public BagFormatter setUnicodePropertyFactory(UnicodeProperty.Factory source) { |
| this.source = source; |
| return this; |
| } |
| |
| public UnicodeProperty.Factory getUnicodePropertyFactory() { |
| if (source == null) source = ICUPropertyFactory.make(); |
| return source; |
| } |
| |
| public BagFormatter () { |
| } |
| |
| public BagFormatter (UnicodeProperty.Factory source) { |
| setUnicodePropertyFactory(source); |
| } |
| |
| public String join(Object o) { |
| return labelVisitor.join(o); |
| } |
| |
| // ===== PRIVATES ===== |
| |
| private Join labelVisitor = new Join(); |
| |
| private boolean mergeRanges = true; |
| private Transliterator showLiteral = null; |
| private Transliterator fixName = null; |
| private boolean showSetAlso = false; |
| |
| private RangeFinder rf = new RangeFinder(); |
| |
| private MessageFormat inOut = new MessageFormat("In {0}, but not in {1}:"); |
| private MessageFormat outIn = new MessageFormat("Not in {0}, but in {1}:"); |
| private MessageFormat inIn = new MessageFormat("In both {0}, and in {1}:"); |
| |
| private MyVisitor mainVisitor = new MyVisitor(); |
| |
| /* |
| private String getLabels(int start, int end) { |
| Set names = new TreeSet(); |
| for (int cp = start; cp <= end; ++cp) { |
| names.add(getLabel(cp)); |
| } |
| return labelVisitor.join(names); |
| } |
| */ |
| |
| private void addMatching( |
| String src, |
| String pattern, |
| String substitute, |
| StringBuffer result) { |
| NameIterator n1 = new NameIterator(src); |
| NameIterator n2 = new NameIterator(pattern); |
| boolean first = true; |
| while (true) { |
| String s1 = n1.next(); |
| if (s1 == null) |
| break; |
| String s2 = n2.next(); |
| if (!first) |
| result.append(" "); |
| first = false; |
| if (s1.equals(s2)) |
| result.append(substitute); |
| else |
| result.append(s1); |
| } |
| } |
| |
| private static NumberFormat nf = |
| NumberFormat.getIntegerInstance(Locale.ENGLISH); |
| static { |
| nf.setGroupingUsed(false); |
| } |
| |
| private int maxWidthOverride = -1; |
| private int maxLabelWidthOverride = -1; |
| |
| public BagFormatter setValueWidthOverride(int maxWidthOverride) { |
| this.maxWidthOverride = maxWidthOverride; |
| return this; |
| } |
| |
| public int getValueWidthOverride() { |
| return maxWidthOverride; |
| } |
| |
| public BagFormatter setLabelWidthOverride(int maxWidthOverride) { |
| this.maxLabelWidthOverride = maxWidthOverride; |
| return this; |
| } |
| |
| public int getLabelWidthOverride() { |
| return maxLabelWidthOverride; |
| } |
| |
| |
| private class MyVisitor extends Visitor { |
| private PrintWriter output; |
| String commentSeparator; |
| int counter; |
| int valueSize; |
| int labelSize; |
| boolean isHtml; |
| boolean inTable = false; |
| |
| public void toOutput(String s) { |
| if (isHtml) { |
| if (inTable) { |
| output.print("</table>"); |
| inTable = false; |
| } |
| output.print("<p>"); |
| } |
| output.print(s); |
| if (isHtml) |
| output.println("</p>"); |
| else |
| output.print(lineSeparator); |
| } |
| |
| public void toTable(String s) { |
| if (isHtml && !inTable) { |
| output.print("<table>"); |
| inTable = true; |
| } |
| output.print(tabber.process(s) + lineSeparator); |
| } |
| |
| public void doAt(Object c, PrintWriter out) { |
| output = out; |
| isHtml = tabber instanceof Tabber.HTMLTabber; |
| counter = 0; |
| |
| tabber.clear(); |
| // old: |
| // 0009..000D ; White_Space # Cc [5] <control-0009>..<control-000D> |
| // new |
| // 0009..000D ; White_Space #Cc [5] <control>..<control> |
| tabber.add(mergeRanges ? 14 : 6,Tabber.LEFT); |
| |
| if (propName.length() > 0) { |
| tabber.add(propName.length() + 2,Tabber.LEFT); |
| } |
| |
| valueSize = maxWidthOverride > 0 ? maxWidthOverride : getValueSource().getMaxWidth(shortValue); |
| |
| if (DEBUG) System.out.println("ValueSize: " + valueSize); |
| if (valueSize > 0) { |
| tabber.add(valueSize + 2,Tabber.LEFT); // value |
| } |
| |
| tabber.add(3,Tabber.LEFT); // comment character |
| |
| labelSize = maxLabelWidthOverride > 0 ? maxLabelWidthOverride : getLabelSource(true).getMaxWidth(shortLabel); |
| if (labelSize > 0) { |
| tabber.add(labelSize + 1,Tabber.LEFT); // value |
| } |
| |
| if (mergeRanges && showCount) { |
| tabber.add(5,Tabber.RIGHT); |
| } |
| |
| if (showLiteral != null) { |
| tabber.add(4,Tabber.LEFT); |
| } |
| //myTabber.add(7,Tabber.LEFT); |
| |
| commentSeparator = (showCount || showLiteral != null |
| || getLabelSource(true) != UnicodeLabel.NULL |
| || getNameSource() != UnicodeLabel.NULL) |
| ? "\t #" : ""; |
| |
| if (DEBUG) System.out.println("Tabber: " + tabber.toString()); |
| if (DEBUG) System.out.println("Tabber: " + tabber.process( |
| "200C..200D\t; White_Space\t #\tCf\t [2]\t ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER")); |
| doAt(c); |
| } |
| |
| @SuppressWarnings("unused") |
| public String format(Object o) { |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| doAt(o); |
| pw.flush(); |
| String result = sw.getBuffer().toString(); |
| pw.close(); |
| return result; |
| } |
| |
| protected void doBefore(Object container, Object o) { |
| if (showSetAlso && container instanceof UnicodeSet) { |
| toOutput("#" + container); |
| } |
| } |
| |
| protected void doBetween(Object container, Object lastItem, Object nextItem) { |
| } |
| |
| protected void doAfter(Object container, Object o) { |
| if (fullTotal != -1 && fullTotal != counter) { |
| if (showTotal) { |
| toOutput(""); |
| toOutput("# The above property value applies to " + nf.format(fullTotal-counter) + " code points not listed here."); |
| toOutput("# Total code points: " + nf.format(fullTotal)); |
| } |
| fullTotal = -1; |
| } else if (showTotal) { |
| toOutput(""); |
| toOutput("# Total code points: " + nf.format(counter)); |
| } |
| } |
| |
| protected void doSimpleAt(Object o) { |
| if (o instanceof Map.Entry) { |
| Map.Entry oo = (Map.Entry)o; |
| Object key = oo.getKey(); |
| Object value = oo.getValue(); |
| doBefore(o, key); |
| doAt(key); |
| output.println("\u2192"); |
| doAt(value); |
| doAfter(o, value); |
| counter++; |
| } else if (o instanceof Visitor.CodePointRange) { |
| doAt((Visitor.CodePointRange) o); |
| } else { |
| String thing = o.toString(); |
| String value = getValueSource() == UnicodeLabel.NULL ? "" : getValueSource().getValue(thing, ",", true); |
| if (getValueSource() != UnicodeLabel.NULL) value = "\t; " + value; |
| String label = getLabelSource(true) == UnicodeLabel.NULL ? "" : getLabelSource(true).getValue(thing, ",", true); |
| if (label.length() != 0) label = " " + label; |
| toTable( |
| hex(thing) |
| + value |
| + commentSeparator |
| + label |
| + insertLiteral(thing) |
| + "\t" |
| + getName(thing)); |
| counter++; |
| } |
| } |
| |
| protected void doAt(Visitor.CodePointRange usi) { |
| if (!mergeRanges) { |
| for (int cp = usi.codepoint; cp <= usi.codepointEnd; ++cp) { |
| showLine(cp, cp); |
| } |
| } else { |
| rf.reset(usi.codepoint, usi.codepointEnd + 1); |
| while (rf.next()) { |
| showLine(rf.start, rf.limit - 1); |
| } |
| } |
| } |
| |
| private void showLine(int start, int end) { |
| String label = getLabelSource(true).getValue(start, shortLabel); |
| String value = getValue(start, shortValue); |
| if (value == NULL_VALUE) return; |
| |
| counter += end - start + 1; |
| String pn = propName; |
| if (pn.length() != 0) { |
| pn = "\t; " + pn; |
| } |
| if (valueSize > 0) { |
| value = "\t; " + value; |
| } else if (value.length() > 0) { |
| throw new IllegalArgumentException("maxwidth bogus " + value + "," + getValueSource().getMaxWidth(shortValue)); |
| } |
| if (labelSize > 0) { |
| label = "\t" + label; |
| } else if (label.length() > 0) { |
| throw new IllegalArgumentException("maxwidth bogus " + label + ", " + getLabelSource(true).getMaxWidth(shortLabel)); |
| } |
| |
| String count = ""; |
| if (mergeRanges && showCount) { |
| if (end == start) count = "\t"; |
| else count = "\t ["+ nf.format(end - start + 1)+ "]"; |
| } |
| |
| toTable( |
| hex(start, end) |
| + pn |
| + value |
| + commentSeparator |
| + label |
| + count |
| + insertLiteral(start, end) |
| + getName("\t ", start, end)); |
| } |
| |
| private String insertLiteral(String thing) { |
| return (showLiteral == null ? "" |
| : " \t(" + showLiteral.transliterate(thing) + ") "); |
| } |
| |
| private String insertLiteral(int start, int end) { |
| return (showLiteral == null ? "" : |
| " \t(" + showLiteral.transliterate(UTF16.valueOf(start)) |
| + ((start != end) |
| ? (".." + showLiteral.transliterate(UTF16.valueOf(end))) |
| : "") |
| + ") "); |
| } |
| /* |
| private String insertLiteral(int cp) { |
| return (showLiteral == null ? "" |
| : " \t(" + showLiteral.transliterate(UTF16.valueOf(cp)) + ") "); |
| } |
| */ |
| } |
| |
| /** |
| * Iterate through a string, breaking at words. |
| * @author Davis |
| */ |
| private static class NameIterator { |
| String source; |
| int position; |
| int limit; |
| |
| NameIterator(String source) { |
| this.source = source; |
| this.limit = source.length(); |
| } |
| /** |
| * Find next word, including trailing spaces |
| * @return the next word |
| */ |
| String next() { |
| if (position >= limit) |
| return null; |
| int pos = source.indexOf(' ', position); |
| if (pos < 0 || pos >= limit) |
| pos = limit; |
| String result = source.substring(position, pos); |
| position = pos + 1; |
| return result; |
| } |
| |
| static int findMatchingEnd(String s1, String s2) { |
| int i = s1.length(); |
| int j = s2.length(); |
| try { |
| while (true) { |
| --i; // decrement both before calling function! |
| --j; |
| if (s1.charAt(i) != s2.charAt(j)) |
| break; |
| } |
| } catch (Exception e) {} // run off start |
| |
| ++i; // counteract increment |
| i = s1.indexOf(' ', i); // move forward to space |
| if (i < 0) |
| return 0; |
| return s1.length() - i; |
| } |
| } |
| |
| private class RangeFinder { |
| int start, limit; |
| private int veryLimit; |
| //String label, value; |
| void reset(int rangeStart, int rangeLimit) { |
| limit = rangeStart; |
| veryLimit = rangeLimit; |
| } |
| boolean next() { |
| if (limit >= veryLimit) |
| return false; |
| start = limit; // set to end of last |
| String label = getLabelSource(false).getValue(limit, true); |
| String value = getValue(limit, true); |
| String breaker = getRangeBreakSource().getValue(limit,true); |
| if (DEBUG && limit < 0x7F) System.out.println("Label: " + label + ", Value: " + value + ", Break: " + breaker); |
| limit++; |
| for (; limit < veryLimit; limit++) { |
| String s = getLabelSource(false).getValue(limit, true); |
| String v = getValue(limit, true); |
| String b = getRangeBreakSource().getValue(limit, true); |
| if (DEBUG && limit < 0x7F) System.out.println("*Label: " + label + ", Value: " + value + ", Break: " + breaker); |
| if (!equalTo(s, label) || !equalTo(v, value) || !equalTo(b, breaker)) break; |
| } |
| // at this point, limit is the first item that has a different label than source |
| // OR, we got to the end, and limit == veryLimit |
| return true; |
| } |
| } |
| |
| boolean equalTo(Object a, Object b) { |
| if (a == b) return true; |
| if (a == null) return false; |
| return a.equals(b); |
| } |
| |
| boolean shortLabel = true; |
| boolean shortValue = true; |
| |
| public String getPrefix() { |
| return prefix; |
| } |
| |
| public String getSuffix() { |
| return suffix; |
| } |
| |
| public BagFormatter setPrefix(String string) { |
| prefix = string; |
| return this; |
| } |
| |
| public BagFormatter setSuffix(String string) { |
| suffix = string; |
| return this; |
| } |
| |
| public boolean isAbbreviated() { |
| return abbreviated; |
| } |
| |
| public BagFormatter setAbbreviated(boolean b) { |
| abbreviated = b; |
| return this; |
| } |
| |
| public UnicodeLabel getLabelSource(boolean visible) { |
| if (labelSource == null) { |
| Map labelMap = new HashMap(); |
| //labelMap.put("Lo","L&"); |
| labelMap.put("Lu","L&"); |
| labelMap.put("Lt","L&"); |
| labelMap.put("Ll","L&"); |
| labelSource = new UnicodeProperty.FilteredProperty( |
| getUnicodePropertyFactory().getProperty("General_Category"), |
| new UnicodeProperty.MapFilter(labelMap) |
| ).setAllowValueAliasCollisions(true); |
| } |
| return labelSource; |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public static void addAll(UnicodeSet source, Collection target) { |
| source.addAllTo(target); |
| } |
| |
| // UTILITIES |
| |
| public static final Transliterator hex = Transliterator.getInstance( |
| "[^\\u0009\\u0020-\\u007E\\u00A0-\\u00FF] hex"); |
| |
| public static BufferedReader openUTF8Reader(String dir, String filename) throws IOException { |
| return openReader(dir,filename,"UTF-8"); |
| } |
| |
| public static BufferedReader openReader(String dir, String filename, String encoding) throws IOException { |
| File file = dir.length() == 0 ? new File(filename) : new File(dir, filename); |
| if (SHOW_FILES && log != null) { |
| log.println("Opening File: " |
| + file.getCanonicalPath()); |
| } |
| return new BufferedReader( |
| new InputStreamReader( |
| new FileInputStream(file), |
| encoding), |
| 4*1024); |
| } |
| |
| public static PrintWriter openUTF8Writer(String dir, String filename) throws IOException { |
| return openWriter(dir,filename,"UTF-8"); |
| } |
| |
| public static PrintWriter openWriter(String dir, String filename, String encoding) throws IOException { |
| File file = new File(dir, filename); |
| if (SHOW_FILES && log != null) { |
| log.println("Creating File: " |
| + file.getCanonicalPath()); |
| } |
| String parentName = file.getParent(); |
| if (parentName != null) { |
| File parent = new File(parentName); |
| parent.mkdirs(); |
| } |
| return new PrintWriter( |
| new BufferedWriter( |
| new OutputStreamWriter( |
| new FileOutputStream(file), |
| encoding), |
| 4*1024)); |
| } |
| public static PrintWriter getLog() { |
| return log; |
| } |
| public BagFormatter setLog(PrintWriter writer) { |
| log = writer; |
| return this; |
| } |
| public String getSeparator() { |
| return separator; |
| } |
| public BagFormatter setSeparator(String string) { |
| separator = string; |
| return this; |
| } |
| public Transliterator getShowLiteral() { |
| return showLiteral; |
| } |
| public BagFormatter setShowLiteral(Transliterator transliterator) { |
| showLiteral = transliterator; |
| return this; |
| } |
| |
| // ===== CONVENIENCES ===== |
| private class Join extends Visitor { |
| StringBuffer output = new StringBuffer(); |
| @SuppressWarnings("unused") |
| int depth = 0; |
| String join (Object o) { |
| output.setLength(0); |
| doAt(o); |
| return output.toString(); |
| } |
| protected void doBefore(Object container, Object item) { |
| ++depth; |
| output.append(prefix); |
| } |
| protected void doAfter(Object container, Object item) { |
| output.append(suffix); |
| --depth; |
| } |
| protected void doBetween(Object container, Object lastItem, Object nextItem) { |
| output.append(separator); |
| } |
| protected void doSimpleAt(Object o) { |
| if (o != null) output.append(o.toString()); |
| } |
| } |
| |
| /** |
| * @param label |
| */ |
| public BagFormatter setLabelSource(UnicodeLabel label) { |
| if (label == null) label = UnicodeLabel.NULL; |
| labelSource = label; |
| return this; |
| } |
| |
| /** |
| * @return the NameLable representing the source |
| */ |
| public UnicodeLabel getNameSource() { |
| if (nameSource == null) { |
| nameSource = new NameLabel(getUnicodePropertyFactory()); |
| } |
| return nameSource; |
| } |
| |
| /** |
| * @param label |
| */ |
| public BagFormatter setNameSource(UnicodeLabel label) { |
| if (label == null) label = UnicodeLabel.NULL; |
| nameSource = label; |
| return this; |
| } |
| |
| /** |
| * @return the UnicodeLabel representing the value |
| */ |
| public UnicodeLabel getValueSource() { |
| if (valueSource == null) valueSource = UnicodeLabel.NULL; |
| return valueSource; |
| } |
| |
| private String getValue(int cp, boolean shortVal) { |
| String result = getValueSource().getValue(cp, shortVal); |
| if (result == null) return NULL_VALUE; |
| if (hexValue) result = hex(result, " "); |
| return result; |
| } |
| |
| /** |
| * @param label |
| */ |
| public BagFormatter setValueSource(UnicodeLabel label) { |
| if (label == null) label = UnicodeLabel.NULL; |
| valueSource = label; |
| return this; |
| } |
| |
| public BagFormatter setValueSource(String label) { |
| return setValueSource(new UnicodeLabel.Constant(label)); |
| } |
| |
| /** |
| * @return true if showCount is true |
| */ |
| public boolean isShowCount() { |
| return showCount; |
| } |
| |
| /** |
| * @param b true to show the count |
| * @return this (for chaining) |
| */ |
| public BagFormatter setShowCount(boolean b) { |
| showCount = b; |
| return this; |
| } |
| |
| /** |
| * @return the property name |
| */ |
| public String getPropName() { |
| return propName; |
| } |
| |
| /** |
| * @param string |
| * @return this (for chaining) |
| */ |
| public BagFormatter setPropName(String string) { |
| if (string == null) string = ""; |
| propName = string; |
| return this; |
| } |
| |
| /** |
| * @return true if this is a hexValue |
| */ |
| public boolean isHexValue() { |
| return hexValue; |
| } |
| |
| /** |
| * @param b |
| * @return this (for chaining) |
| */ |
| public BagFormatter setHexValue(boolean b) { |
| hexValue = b; |
| return this; |
| } |
| |
| /** |
| * @return the full total |
| */ |
| public int getFullTotal() { |
| return fullTotal; |
| } |
| |
| /** |
| * @param i set the full total |
| * @return this (for chaining) |
| */ |
| public BagFormatter setFullTotal(int i) { |
| fullTotal = i; |
| return this; |
| } |
| |
| /** |
| * @return the line separator |
| */ |
| public String getLineSeparator() { |
| return lineSeparator; |
| } |
| |
| /** |
| * @param string |
| * @return this (for chaining) |
| */ |
| public BagFormatter setLineSeparator(String string) { |
| lineSeparator = string; |
| return this; |
| } |
| |
| /** |
| * @return the UnicodeLabel representing the range break source |
| */ |
| public UnicodeLabel getRangeBreakSource() { |
| if (rangeBreakSource == null) { |
| Map labelMap = new HashMap(); |
| // reflects the code point types on p 25 |
| labelMap.put("Lo", "G&"); |
| labelMap.put("Lm", "G&"); |
| labelMap.put("Lu", "G&"); |
| labelMap.put("Lt", "G&"); |
| labelMap.put("Ll", "G&"); |
| labelMap.put("Mn", "G&"); |
| labelMap.put("Me", "G&"); |
| labelMap.put("Mc", "G&"); |
| labelMap.put("Nd", "G&"); |
| labelMap.put("Nl", "G&"); |
| labelMap.put("No", "G&"); |
| labelMap.put("Zs", "G&"); |
| labelMap.put("Pd", "G&"); |
| labelMap.put("Ps", "G&"); |
| labelMap.put("Pe", "G&"); |
| labelMap.put("Pc", "G&"); |
| labelMap.put("Po", "G&"); |
| labelMap.put("Pi", "G&"); |
| labelMap.put("Pf", "G&"); |
| labelMap.put("Sm", "G&"); |
| labelMap.put("Sc", "G&"); |
| labelMap.put("Sk", "G&"); |
| labelMap.put("So", "G&"); |
| |
| labelMap.put("Zl", "Cf"); |
| labelMap.put("Zp", "Cf"); |
| |
| rangeBreakSource = |
| new UnicodeProperty |
| .FilteredProperty( |
| getUnicodePropertyFactory().getProperty( |
| "General_Category"), |
| new UnicodeProperty.MapFilter(labelMap)) |
| .setAllowValueAliasCollisions(true); |
| |
| /* |
| "Cn", // = Other, Not Assigned 0 |
| "Cc", // = Other, Control 15 |
| "Cf", // = Other, Format 16 |
| UnicodeProperty.UNUSED, // missing |
| "Co", // = Other, Private Use 18 |
| "Cs", // = Other, Surrogate 19 |
| */ |
| } |
| return rangeBreakSource; |
| } |
| |
| /** |
| * @param label |
| */ |
| public BagFormatter setRangeBreakSource(UnicodeLabel label) { |
| if (label == null) label = UnicodeLabel.NULL; |
| rangeBreakSource = label; |
| return this; |
| } |
| |
| /** |
| * @return Returns the fixName. |
| */ |
| public Transliterator getFixName() { |
| return fixName; |
| } |
| /** |
| * @param fixName The fixName to set. |
| */ |
| public BagFormatter setFixName(Transliterator fixName) { |
| this.fixName = fixName; |
| return this; |
| } |
| |
| public Tabber getTabber() { |
| return tabber; |
| } |
| |
| public void setTabber(Tabber tabber) { |
| this.tabber = tabber; |
| } |
| |
| public boolean isShowTotal() { |
| return showTotal; |
| } |
| |
| public void setShowTotal(boolean showTotal) { |
| this.showTotal = showTotal; |
| } |
| } |