blob: aaa309f5500ffc484e4adf159c27f09315ea8dfb [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 1996-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.test.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
import com.ibm.icu.dev.test.util.CollectionUtilities.InverseMatcher;
import com.ibm.icu.dev.test.util.CollectionUtilities.ObjectMatcher;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.text.SymbolTable;
import com.ibm.icu.text.UFormat;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeMatcher;
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.text.UnicodeSetIterator;
public abstract class UnicodeProperty extends UnicodeLabel {
public static final UnicodeSet UNASSIGNED = new UnicodeSet("[:gc=unassigned:]").freeze();
public static final UnicodeSet PRIVATE_USE = new UnicodeSet("[:gc=privateuse:]").freeze();
public static final UnicodeSet SURROGATE = new UnicodeSet("[:gc=surrogate:]").freeze();
public static final UnicodeSet SPECIALS = new UnicodeSet(UNASSIGNED).addAll(PRIVATE_USE).addAll(SURROGATE).freeze();
public static final int SAMPLE_UNASSIGNED = UNASSIGNED.charAt(0);
public static final int SAMPLE_PRIVATE_USE = 0xE000;
public static final int SAMPLE_SURROGATE = 0xD800;
public static final UnicodeSet STUFF_TO_TEST = new UnicodeSet(SPECIALS).complement()
.add(SAMPLE_UNASSIGNED).add(SAMPLE_PRIVATE_USE).add(SAMPLE_SURROGATE).freeze();
public static final UnicodeSet STUFF_TO_TEST_WITH_UNASSIGNED = new UnicodeSet("[:any:]").freeze();
public static boolean DEBUG = false;
public static String CHECK_NAME = "FC_NFKC_Closure";
public static int CHECK_VALUE = 0x037A;
private String name;
private String firstNameAlias = null;
private int type;
private Map valueToFirstValueAlias = null;
private boolean hasUniformUnassigned = false;
/*
* Name: Unicode_1_Name Name: ISO_Comment Name: Name Name: Unicode_1_Name
*
*/
public static final int UNKNOWN = 0, BINARY = 2, EXTENDED_BINARY = 3,
ENUMERATED = 4, EXTENDED_ENUMERATED = 5, CATALOG = 6,
EXTENDED_CATALOG = 7, MISC = 8, EXTENDED_MISC = 9, STRING = 10,
EXTENDED_STRING = 11, NUMERIC = 12, EXTENDED_NUMERIC = 13,
START_TYPE = 2, LIMIT_TYPE = 14, EXTENDED_MASK = 1,
CORE_MASK = ~EXTENDED_MASK, BINARY_MASK = (1 << BINARY)
| (1 << EXTENDED_BINARY), STRING_MASK = (1 << STRING)
| (1 << EXTENDED_STRING),
STRING_OR_MISC_MASK = (1 << STRING) | (1 << EXTENDED_STRING)
| (1 << MISC) | (1 << EXTENDED_MISC),
ENUMERATED_OR_CATALOG_MASK = (1 << ENUMERATED)
| (1 << EXTENDED_ENUMERATED) | (1 << CATALOG)
| (1 << EXTENDED_CATALOG);
private static final String[] TYPE_NAMES = { "Unknown", "Unknown",
"Binary", "Extended Binary", "Enumerated", "Extended Enumerated",
"Catalog", "Extended Catalog", "Miscellaneous",
"Extended Miscellaneous", "String", "Extended String", "Numeric",
"Extended Numeric", };
public static String getTypeName(int propType) {
return TYPE_NAMES[propType];
}
public final String getName() {
return name;
}
public final int getType() {
return type;
}
public String getTypeName() {
return TYPE_NAMES[type];
}
public final boolean isType(int mask) {
return ((1 << type) & mask) != 0;
}
protected final void setName(String string) {
if (string == null)
throw new IllegalArgumentException("Name must not be null");
name = string;
}
protected final void setType(int i) {
type = i;
}
public String getVersion() {
return _getVersion();
}
public String getValue(int codepoint) {
if (DEBUG && CHECK_VALUE == codepoint && CHECK_NAME.equals(getName())) {
String value = _getValue(codepoint);
System.out.println(getName() + "(" + Utility.hex(codepoint) + "):"
+ (getType() == STRING ? Utility.hex(value) : value));
return value;
}
return _getValue(codepoint);
}
// public String getValue(int codepoint, boolean isShort) {
// return getValue(codepoint);
// }
public List getNameAliases(List result) {
if (result == null)
result = new ArrayList(1);
return _getNameAliases(result);
}
public List getValueAliases(String valueAlias, List result) {
if (result == null)
result = new ArrayList(1);
result = _getValueAliases(valueAlias, result);
if (!result.contains(valueAlias)) { // FIX && type < NUMERIC
result = _getValueAliases(valueAlias, result); // for debugging
throw new IllegalArgumentException("Internal error: " + getName()
+ " doesn't contain " + valueAlias + ": "
+ new BagFormatter().join(result));
}
return result;
}
public List getAvailableValues(List result) {
if (result == null)
result = new ArrayList(1);
return _getAvailableValues(result);
}
protected abstract String _getVersion();
protected abstract String _getValue(int codepoint);
protected abstract List _getNameAliases(List result);
protected abstract List _getValueAliases(String valueAlias, List result);
protected abstract List _getAvailableValues(List result);
// conveniences
public final List getNameAliases() {
return getNameAliases(null);
}
public final List getValueAliases(String valueAlias) {
return getValueAliases(valueAlias, null);
}
public final List getAvailableValues() {
return getAvailableValues(null);
}
public final String getValue(int codepoint, boolean getShortest) {
String result = getValue(codepoint);
if (type >= MISC || result == null || !getShortest)
return result;
return getFirstValueAlias(result);
}
public final String getFirstNameAlias() {
if (firstNameAlias == null) {
firstNameAlias = (String) getNameAliases().get(0);
}
return firstNameAlias;
}
public final String getFirstValueAlias(String value) {
if (valueToFirstValueAlias == null)
_getFirstValueAliasCache();
return valueToFirstValueAlias.get(value).toString();
}
private void _getFirstValueAliasCache() {
maxValueWidth = 0;
maxFirstValueAliasWidth = 0;
valueToFirstValueAlias = new HashMap(1);
Iterator it = getAvailableValues().iterator();
while (it.hasNext()) {
String value = (String) it.next();
String first = (String) getValueAliases(value).get(0);
if (first == null) { // internal error
throw new IllegalArgumentException(
"Value not in value aliases: " + value);
}
if (DEBUG && CHECK_NAME.equals(getName())) {
System.out.println("First Alias: " + getName() + ": " + value
+ " => " + first
+ new BagFormatter().join(getValueAliases(value)));
}
valueToFirstValueAlias.put(value, first);
if (value.length() > maxValueWidth) {
maxValueWidth = value.length();
}
if (first.length() > maxFirstValueAliasWidth) {
maxFirstValueAliasWidth = first.length();
}
}
}
private int maxValueWidth = -1;
private int maxFirstValueAliasWidth = -1;
public int getMaxWidth(boolean getShortest) {
if (maxValueWidth < 0)
_getFirstValueAliasCache();
if (getShortest)
return maxFirstValueAliasWidth;
return maxValueWidth;
}
public final UnicodeSet getSet(String propertyValue) {
return getSet(propertyValue, null);
}
public final UnicodeSet getSet(PatternMatcher matcher) {
return getSet(matcher, null);
}
public final UnicodeSet getSet(String propertyValue, UnicodeSet result) {
return getSet(new SimpleMatcher(propertyValue,
isType(STRING_OR_MISC_MASK) ? null : PROPERTY_COMPARATOR),
result);
}
private UnicodeMap unicodeMap = null;
public static final String UNUSED = "??";
public final UnicodeSet getSet(PatternMatcher matcher, UnicodeSet result) {
if (result == null)
result = new UnicodeSet();
boolean uniformUnassigned = hasUniformUnassigned();
if (isType(STRING_OR_MISC_MASK)) {
for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
int i = usi.codepoint;
String value = getValue(i);
if (value != null && matcher.matches(value)) {
result.add(i);
}
}
return addUntested(result, uniformUnassigned);
}
List temp = new ArrayList(1); // to avoid reallocating...
UnicodeMap um = getUnicodeMap_internal();
Iterator it = um.getAvailableValues(null).iterator();
main: while (it.hasNext()) {
String value = (String) it.next();
temp.clear();
Iterator it2 = getValueAliases(value, temp).iterator();
while (it2.hasNext()) {
String value2 = (String) it2.next();
// System.out.println("Values:" + value2);
if (matcher.matches(value2)
|| matcher.matches(toSkeleton(value2))) {
um.keySet(value, result);
continue main;
}
}
}
return result;
}
/*
* public UnicodeSet getMatchSet(UnicodeSet result) { if (result == null)
* result = new UnicodeSet(); addAll(matchIterator, result); return result; }
*
* public void setMatchSet(UnicodeSet set) { matchIterator = new
* UnicodeSetIterator(set); }
*/
/**
* Utility for debugging
*/
public static String getStack() {
Exception e = new Exception();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
return "Showing Stack with fake " + sw.getBuffer().toString();
}
// TODO use this instead of plain strings
public static class Name implements Comparable {
private String skeleton;
private String pretty;
public final int RAW = 0, TITLE = 1, NORMAL = 2;
public Name(String name, int style) {
if (name == null)
name = "";
if (style == RAW) {
skeleton = pretty = name;
} else {
pretty = regularize(name, style == TITLE);
skeleton = toSkeleton(pretty);
}
}
public int compareTo(Object o) {
return skeleton.compareTo(((Name) o).skeleton);
}
public boolean equals(Object o) {
return skeleton.equals(((Name) o).skeleton);
}
public int hashCode() {
return skeleton.hashCode();
}
public String toString() {
return pretty;
}
}
/**
* @return the unicode map
*/
public UnicodeMap getUnicodeMap() {
return getUnicodeMap(false);
}
/**
* @return the unicode map
*/
public UnicodeMap getUnicodeMap(boolean getShortest) {
if (!getShortest)
return (UnicodeMap) getUnicodeMap_internal().cloneAsThawed();
UnicodeMap result = new UnicodeMap();
boolean uniformUnassigned = hasUniformUnassigned();
for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
int i = usi.codepoint;
// if (DEBUG && i == 0x41) System.out.println(i + "\t" +
// getValue(i));
String value = getValue(i, true);
result.put(i, value);
}
return addUntested(result, uniformUnassigned);
}
/**
* @return the unicode map
*/
protected UnicodeMap getUnicodeMap_internal() {
if (unicodeMap == null)
unicodeMap = _getUnicodeMap();
return unicodeMap;
}
protected UnicodeMap _getUnicodeMap() {
UnicodeMap result = new UnicodeMap();
HashMap myIntern = new HashMap();
boolean uniformUnassigned = hasUniformUnassigned();
for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
int i = usi.codepoint;
// if (DEBUG && i == 0x41) System.out.println(i + "\t" +
// getValue(i));
String value = getValue(i);
String iValue = (String) myIntern.get(value);
if (iValue == null)
myIntern.put(value, iValue = value);
result.put(i, iValue);
}
addUntested(result, uniformUnassigned);
if (DEBUG) {
for (UnicodeSetIterator usi = getStuffToTest(uniformUnassigned); usi.next();) { // int i = 0; i <= 0x10FFFF; ++i
int i = usi.codepoint;
// if (DEBUG && i == 0x41) System.out.println(i + "\t" +
// getValue(i));
String value = getValue(i);
String resultValue = (String) result.getValue(i);
if (!value.equals(resultValue)) {
throw new RuntimeException("Value failure at: "
+ Utility.hex(i));
}
}
}
if (DEBUG && CHECK_NAME.equals(getName())) {
System.out.println(getName() + ":\t" + getClass().getName() + "\t"
+ getVersion());
System.out.println(getStack());
System.out.println(result);
}
return result;
}
private static UnicodeSetIterator getStuffToTest(boolean uniformUnassigned) {
return new UnicodeSetIterator(uniformUnassigned ? STUFF_TO_TEST : STUFF_TO_TEST_WITH_UNASSIGNED);
}
/**
* Really ought to create a Collection UniqueList, that forces uniqueness.
* But for now...
*/
public static Collection addUnique(Object obj, Collection result) {
if (obj != null && !result.contains(obj))
result.add(obj);
return result;
}
/**
* Utility for managing property & non-string value aliases
*/
public static final Comparator PROPERTY_COMPARATOR = new Comparator() {
public int compare(Object o1, Object o2) {
return compareNames((String) o1, (String) o2);
}
};
/**
* Utility for managing property & non-string value aliases
*
*/
// TODO optimize
public static boolean equalNames(String a, String b) {
if (a == b)
return true;
if (a == null)
return false;
return toSkeleton(a).equals(toSkeleton(b));
}
/**
* Utility for managing property & non-string value aliases
*/
// TODO optimize
public static int compareNames(String a, String b) {
if (a == b)
return 0;
if (a == null)
return -1;
if (b == null)
return 1;
return toSkeleton(a).compareTo(toSkeleton(b));
}
/**
* Utility for managing property & non-string value aliases
*/
// TODO account for special names, tibetan, hangul
public static String toSkeleton(String source) {
if (source == null)
return null;
StringBuffer skeletonBuffer = new StringBuffer();
boolean gotOne = false;
// remove spaces, '_', '-'
// we can do this with char, since no surrogates are involved
for (int i = 0; i < source.length(); ++i) {
char ch = source.charAt(i);
if (i > 0 && (ch == '_' || ch == ' ' || ch == '-')) {
gotOne = true;
} else {
char ch2 = Character.toLowerCase(ch);
if (ch2 != ch) {
gotOne = true;
skeletonBuffer.append(ch2);
} else {
skeletonBuffer.append(ch);
}
}
}
if (!gotOne)
return source; // avoid string creation
return skeletonBuffer.toString();
}
// get the name skeleton
public static String toNameSkeleton(String source) {
if (source == null)
return null;
StringBuffer result = new StringBuffer();
// remove spaces, medial '-'
// we can do this with char, since no surrogates are involved
for (int i = 0; i < source.length(); ++i) {
char ch = source.charAt(i);
if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z')
|| ch == '<' || ch == '>') {
result.append(ch);
} else if (ch == ' ') {
// don't copy ever
} else if (ch == '-') {
// only copy non-medials AND trailing O-E
if (0 == i
|| i == source.length() - 1
|| source.charAt(i - 1) == ' '
|| source.charAt(i + 1) == ' '
|| (i == source.length() - 2
&& source.charAt(i - 1) == 'O' && source
.charAt(i + 1) == 'E')) {
System.out.println("****** EXCEPTION " + source);
result.append(ch);
}
// otherwise don't copy
} else {
throw new IllegalArgumentException("Illegal Name Char: U+"
+ Utility.hex(ch) + ", " + ch);
}
}
return result.toString();
}
/**
* These routines use the Java functions, because they only need to act on
* ASCII Changes space, - into _, inserts _ between lower and UPPER.
*/
public static String regularize(String source, boolean titlecaseStart) {
if (source == null)
return source;
/*
* if (source.equals("noBreak")) { // HACK if (titlecaseStart) return
* "NoBreak"; return source; }
*/
StringBuffer result = new StringBuffer();
int lastCat = -1;
boolean haveFirstCased = true;
for (int i = 0; i < source.length(); ++i) {
char c = source.charAt(i);
if (c == ' ' || c == '-' || c == '_') {
c = '_';
haveFirstCased = true;
}
if (c == '=')
haveFirstCased = true;
int cat = Character.getType(c);
if (lastCat == Character.LOWERCASE_LETTER
&& cat == Character.UPPERCASE_LETTER) {
result.append('_');
}
if (haveFirstCased
&& (cat == Character.LOWERCASE_LETTER
|| cat == Character.TITLECASE_LETTER || cat == Character.UPPERCASE_LETTER)) {
if (titlecaseStart) {
c = Character.toUpperCase(c);
}
haveFirstCased = false;
}
result.append(c);
lastCat = cat;
}
return result.toString();
}
/**
* Utility function for comparing codepoint to string without generating new
* string.
*
* @param codepoint
* @param other
* @return true if the codepoint equals the string
*/
public static final boolean equals(int codepoint, String other) {
if (other == null) return false;
if (other.length() == 1) {
return codepoint == other.charAt(0);
}
if (other.length() == 2) {
return other.equals(UTF16.valueOf(codepoint));
}
return false;
}
/**
* Utility function for comparing objects that may be null
* string.
*/
public static final <T extends Object> boolean equals(T a, T b) {
return a == null ? b == null
: b == null ? false
: a.equals(b);
}
/**
* Utility that should be on UnicodeSet
*
* @param source
* @param result
*/
static public void addAll(UnicodeSetIterator source, UnicodeSet result) {
while (source.nextRange()) {
if (source.codepoint == UnicodeSetIterator.IS_STRING) {
result.add(source.string);
} else {
result.add(source.codepoint, source.codepointEnd);
}
}
}
/**
* Really ought to create a Collection UniqueList, that forces uniqueness.
* But for now...
*/
public static Collection addAllUnique(Collection source, Collection result) {
for (Iterator it = source.iterator(); it.hasNext();) {
addUnique(it.next(), result);
}
return result;
}
/**
* Really ought to create a Collection UniqueList, that forces uniqueness.
* But for now...
*/
public static Collection addAllUnique(Object[] source, Collection result) {
for (int i = 0; i < source.length; ++i) {
addUnique(source[i], result);
}
return result;
}
static public class Factory {
static boolean DEBUG = false;
Map canonicalNames = new TreeMap();
Map skeletonNames = new TreeMap();
Map propertyCache = new HashMap(1);
public final Factory add(UnicodeProperty sp) {
canonicalNames.put(sp.getName(), sp);
List c = sp.getNameAliases(new ArrayList(1));
Iterator it = c.iterator();
while (it.hasNext()) {
skeletonNames.put(toSkeleton((String) it.next()), sp);
}
return this;
}
public final UnicodeProperty getProperty(String propertyAlias) {
return (UnicodeProperty) skeletonNames
.get(toSkeleton(propertyAlias));
}
public final List getAvailableNames() {
return getAvailableNames(null);
}
public final List getAvailableNames(List result) {
if (result == null)
result = new ArrayList(1);
Iterator it = canonicalNames.keySet().iterator();
while (it.hasNext()) {
addUnique(it.next(), result);
}
return result;
}
public final List getAvailableNames(int propertyTypeMask) {
return getAvailableNames(propertyTypeMask, null);
}
public final List getAvailableNames(int propertyTypeMask, List result) {
if (result == null)
result = new ArrayList(1);
Iterator it = canonicalNames.keySet().iterator();
while (it.hasNext()) {
String item = (String) it.next();
UnicodeProperty property = getProperty(item);
if (DEBUG)
System.out.println("Properties: " + item + ","
+ property.getType());
if (!property.isType(propertyTypeMask)) {
// System.out.println("Masking: " + property.getType() + ","
// + propertyTypeMask);
continue;
}
addUnique(property.getName(), result);
}
return result;
}
InversePatternMatcher inverseMatcher = new InversePatternMatcher();
/**
* Format is: propname ('=' | '!=') propvalue ( '|' propValue )*
*/
public final UnicodeSet getSet(String propAndValue,
PatternMatcher matcher, UnicodeSet result) {
int equalPos = propAndValue.indexOf('=');
String prop = propAndValue.substring(0, equalPos);
String value = propAndValue.substring(equalPos + 1);
boolean negative = false;
if (prop.endsWith("!")) {
prop = prop.substring(0, prop.length() - 1);
negative = true;
}
prop = prop.trim();
UnicodeProperty up = getProperty(prop);
if (matcher == null) {
matcher = new SimpleMatcher(value, up
.isType(STRING_OR_MISC_MASK) ? null
: PROPERTY_COMPARATOR);
}
if (negative) {
inverseMatcher.set(matcher);
matcher = inverseMatcher;
}
return up.getSet(matcher.set(value), result);
}
public final UnicodeSet getSet(String propAndValue,
PatternMatcher matcher) {
return getSet(propAndValue, matcher, null);
}
public final UnicodeSet getSet(String propAndValue) {
return getSet(propAndValue, null, null);
}
public final SymbolTable getSymbolTable(String prefix) {
return new PropertySymbolTable(prefix);
}
private class MyXSymbolTable extends UnicodeSet.XSymbolTable {
public boolean applyPropertyAlias(String propertyName,
String propertyValue, UnicodeSet result) {
if (false)
System.out.println(propertyName + "=" + propertyValue);
UnicodeProperty prop = getProperty(propertyName);
if (prop == null)
return false;
result.clear();
UnicodeSet x = prop.getSet(propertyValue, result);
return x.size() != 0;
}
}
public final UnicodeSet.XSymbolTable getXSymbolTable() {
return new MyXSymbolTable();
}
private class PropertySymbolTable implements SymbolTable {
static final boolean DEBUG = false;
private String prefix;
RegexMatcher regexMatcher = new RegexMatcher();
PropertySymbolTable(String prefix) {
this.prefix = prefix;
}
public char[] lookup(String s) {
if (DEBUG)
System.out.println("\t(" + prefix + ")Looking up " + s);
// ensure, again, that prefix matches
int start = prefix.length();
if (!s.regionMatches(true, 0, prefix, 0, start))
return null;
int pos = s.indexOf(':', start);
if (pos < 0) { // should never happen
throw new IllegalArgumentException(
"Internal Error: missing =: " + s + "\r\n");
}
UnicodeProperty prop = getProperty(s.substring(start, pos));
if (prop == null) {
throw new IllegalArgumentException("Invalid Property in: "
+ s + "\r\nUse " + showSet(getAvailableNames()));
}
String value = s.substring(pos + 1);
UnicodeSet set;
if (value.startsWith("\u00AB")) { // regex!
set = prop.getSet(regexMatcher.set(value.substring(1, value
.length() - 1)));
} else {
set = prop.getSet(value);
}
if (set.size() == 0) {
throw new IllegalArgumentException(
"Empty Property-Value in: " + s + "\r\nUse "
+ showSet(prop.getAvailableValues()));
}
if (DEBUG)
System.out.println("\t(" + prefix + ")Returning "
+ set.toPattern(true));
return set.toPattern(true).toCharArray(); // really ugly
}
private String showSet(List list) {
StringBuffer result = new StringBuffer("[");
boolean first = true;
for (Iterator it = list.iterator(); it.hasNext();) {
if (!first)
result.append(", ");
else
first = false;
result.append(it.next().toString());
}
result.append("]");
return result.toString();
}
public UnicodeMatcher lookupMatcher(int ch) {
return null;
}
public String parseReference(String text, ParsePosition pos,
int limit) {
if (DEBUG)
System.out.println("\t(" + prefix + ")Parsing <"
+ text.substring(pos.getIndex(), limit) + ">");
int start = pos.getIndex();
// ensure that it starts with 'prefix'
if (!text
.regionMatches(true, start, prefix, 0, prefix.length()))
return null;
start += prefix.length();
// now see if it is of the form identifier:identifier
int i = getIdentifier(text, start, limit);
if (i == start)
return null;
String prop = text.substring(start, i);
String value = "true";
if (i < limit) {
if (text.charAt(i) == ':') {
int j;
if (text.charAt(i + 1) == '\u00AB') { // regular
// expression
j = text.indexOf('\u00BB', i + 2) + 1; // include
// last
// character
if (j <= 0)
return null;
} else {
j = getIdentifier(text, i + 1, limit);
}
value = text.substring(i + 1, j);
i = j;
}
}
pos.setIndex(i);
if (DEBUG)
System.out.println("\t(" + prefix + ")Parsed <" + prop
+ ">=<" + value + ">");
return prefix + prop + ":" + value;
}
private int getIdentifier(String text, int start, int limit) {
if (DEBUG)
System.out.println("\tGetID <"
+ text.substring(start, limit) + ">");
int cp = 0;
int i;
for (i = start; i < limit; i += UTF16.getCharCount(cp)) {
cp = UTF16.charAt(text, i);
if (!com.ibm.icu.lang.UCharacter
.isUnicodeIdentifierPart(cp)
&& cp != '.') {
break;
}
}
if (DEBUG)
System.out.println("\tGotID <" + text.substring(start, i)
+ ">");
return i;
}
}
}
public static class FilteredProperty extends UnicodeProperty {
private UnicodeProperty property;
protected StringFilter filter;
protected UnicodeSetIterator matchIterator = new UnicodeSetIterator(
new UnicodeSet(0, 0x10FFFF));
protected HashMap backmap;
boolean allowValueAliasCollisions = false;
public FilteredProperty(UnicodeProperty property, StringFilter filter) {
this.property = property;
this.filter = filter;
}
public StringFilter getFilter() {
return filter;
}
public UnicodeProperty setFilter(StringFilter filter) {
this.filter = filter;
return this;
}
List temp = new ArrayList(1);
public List _getAvailableValues(List result) {
temp.clear();
return filter.addUnique(property.getAvailableValues(temp), result);
}
public List _getNameAliases(List result) {
temp.clear();
return filter.addUnique(property.getNameAliases(temp), result);
}
public String _getValue(int codepoint) {
return filter.remap(property.getValue(codepoint));
}
public List _getValueAliases(String valueAlias, List result) {
if (backmap == null) {
backmap = new HashMap(1);
temp.clear();
Iterator it = property.getAvailableValues(temp).iterator();
while (it.hasNext()) {
String item = (String) it.next();
String mappedItem = filter.remap(item);
if (backmap.get(mappedItem) != null
&& !allowValueAliasCollisions) {
throw new IllegalArgumentException(
"Filter makes values collide! " + item + ", "
+ mappedItem);
}
backmap.put(mappedItem, item);
}
}
valueAlias = (String) backmap.get(valueAlias);
temp.clear();
return filter.addUnique(property.getValueAliases(valueAlias, temp),
result);
}
public String _getVersion() {
return property.getVersion();
}
public boolean isAllowValueAliasCollisions() {
return allowValueAliasCollisions;
}
public FilteredProperty setAllowValueAliasCollisions(boolean b) {
allowValueAliasCollisions = b;
return this;
}
}
public static abstract class StringFilter implements Cloneable {
public abstract String remap(String original);
public final List addUnique(Collection source, List result) {
if (result == null)
result = new ArrayList(1);
Iterator it = source.iterator();
while (it.hasNext()) {
UnicodeProperty.addUnique(remap((String) it.next()), result);
}
return result;
}
/*
* public Object clone() { try { return super.clone(); } catch
* (CloneNotSupportedException e) { throw new
* IllegalStateException("Should never happen."); } }
*/
}
public static class MapFilter extends StringFilter {
private Map valueMap;
public MapFilter(Map valueMap) {
this.valueMap = valueMap;
}
public String remap(String original) {
Object changed = valueMap.get(original);
return changed == null ? original : (String) changed;
}
public Map getMap() {
return valueMap;
}
}
public interface PatternMatcher extends ObjectMatcher {
public PatternMatcher set(String pattern);
}
public static class InversePatternMatcher extends InverseMatcher implements
PatternMatcher {
PatternMatcher other;
public PatternMatcher set(PatternMatcher toInverse) {
other = toInverse;
return this;
}
public boolean matches(Object value) {
return !other.matches(value);
}
public PatternMatcher set(String pattern) {
other.set(pattern);
return this;
}
}
public static class SimpleMatcher implements PatternMatcher {
Comparator comparator;
String pattern;
public SimpleMatcher(String pattern, Comparator comparator) {
this.comparator = comparator;
this.pattern = pattern;
}
public boolean matches(Object value) {
if (comparator == null)
return pattern.equals(value);
return comparator.compare(pattern, value) == 0;
}
public PatternMatcher set(String pattern) {
this.pattern = pattern;
return this;
}
}
public static class RegexMatcher implements UnicodeProperty.PatternMatcher {
private java.util.regex.Matcher matcher;
public UnicodeProperty.PatternMatcher set(String pattern) {
matcher = Pattern.compile(pattern).matcher("");
return this;
}
UFormat foo;
public boolean matches(Object value) {
matcher.reset(value.toString());
return matcher.find();
}
}
public static abstract class BaseProperty extends UnicodeProperty {
private static final String[] NO_VALUES = {"No", "N", "F", "False"};
private static final String[] YES_VALUES = {"Yes", "Y", "T", "True"};
/**
*
*/
private static final String[][] YES_NO_ALIASES = new String[][] {YES_VALUES, NO_VALUES};
protected List propertyAliases = new ArrayList(1);
protected Map toValueAliases;
protected String version;
public BaseProperty setMain(String alias, String shortAlias,
int propertyType, String version) {
setName(alias);
setType(propertyType);
propertyAliases.add(shortAlias);
propertyAliases.add(alias);
if (propertyType == BINARY) {
addValueAliases(YES_NO_ALIASES, false);
}
this.version = version;
return this;
}
public String _getVersion() {
return version;
}
public List _getNameAliases(List result) {
addAllUnique(propertyAliases, result);
return result;
}
public BaseProperty addValueAliases(String[][] valueAndAlternates,
boolean errorIfCant) {
if (toValueAliases == null)
_fixValueAliases();
for (int i = 0; i < valueAndAlternates.length; ++i) {
for (int j = 1; j < valueAndAlternates[0].length; ++j) {
addValueAlias(valueAndAlternates[i][0],
valueAndAlternates[i][j], errorIfCant);
}
}
return this;
}
public void addValueAlias(String value, String valueAlias,
boolean errorIfCant) {
List result = (List) toValueAliases.get(value);
if (result == null && !errorIfCant)
return;
addUnique(value, result);
addUnique(valueAlias, result);
}
protected List _getValueAliases(String valueAlias, List result) {
if (toValueAliases == null)
_fixValueAliases();
List a = (List) toValueAliases.get(valueAlias);
if (a != null)
addAllUnique(a, result);
return result;
}
protected void _fixValueAliases() {
if (toValueAliases == null)
toValueAliases = new HashMap(1);
for (Iterator it = getAvailableValues().iterator(); it.hasNext();) {
Object value = it.next();
_ensureValueInAliases(value);
}
}
protected void _ensureValueInAliases(Object value) {
List result = (List) toValueAliases.get(value);
if (result == null)
toValueAliases.put(value, result = new ArrayList(1));
addUnique(value, result);
}
public BaseProperty swapFirst2ValueAliases() {
for (Iterator it = toValueAliases.keySet().iterator(); it.hasNext();) {
List list = (List) toValueAliases.get(it.next());
if (list.size() < 2)
continue;
Object first = list.get(0);
list.set(0, list.get(1));
list.set(1, first);
}
return this;
}
/**
* @param string
* @return
*/
public UnicodeProperty addName(String string) {
throw new UnsupportedOperationException();
}
}
public static abstract class SimpleProperty extends BaseProperty {
List values;
public UnicodeProperty addName(String alias) {
propertyAliases.add(alias);
return this;
}
public SimpleProperty setValues(String valueAlias) {
_addToValues(valueAlias, null);
return this;
}
public SimpleProperty addAliases(String valueAlias, String... aliases) {
_addToValues(valueAlias, null);
return this;
}
public SimpleProperty setValues(String[] valueAliases,
String[] alternateValueAliases) {
for (int i = 0; i < valueAliases.length; ++i) {
if (valueAliases[i].equals(UNUSED))
continue;
_addToValues(
valueAliases[i],
alternateValueAliases != null ? alternateValueAliases[i]
: null);
}
return this;
}
public SimpleProperty setValues(List valueAliases) {
this.values = new ArrayList(valueAliases);
for (Iterator it = this.values.iterator(); it.hasNext();) {
_addToValues((String) it.next(), null);
}
return this;
}
public List _getAvailableValues(List result) {
if (values == null)
_fillValues();
result.addAll(values);
return result;
}
protected void _fillValues() {
List newvalues = (List) getUnicodeMap_internal()
.getAvailableValues(new ArrayList());
for (Iterator it = newvalues.iterator(); it.hasNext();) {
_addToValues((String) it.next(), null);
}
}
private void _addToValues(String item, String alias) {
if (values == null)
values = new ArrayList(1);
if (toValueAliases == null)
_fixValueAliases();
addUnique(item, values);
_ensureValueInAliases(item);
addValueAlias(item, alias, true);
}
/* public String _getVersion() {
return version;
}
*/
}
public static class UnicodeMapProperty extends BaseProperty {
/*
* Example of usage:
* new UnicodeProperty.UnicodeMapProperty() {
{
unicodeMap = new UnicodeMap();
unicodeMap.setErrorOnReset(true);
unicodeMap.put(0xD, "CR");
unicodeMap.put(0xA, "LF");
UnicodeProperty cat = getProperty("General_Category");
UnicodeSet temp = cat.getSet("Line_Separator")
.addAll(cat.getSet("Paragraph_Separator"))
.addAll(cat.getSet("Control"))
.addAll(cat.getSet("Format"))
.remove(0xD).remove(0xA).remove(0x200C).remove(0x200D);
unicodeMap.putAll(temp, "Control");
UnicodeSet graphemeExtend = getProperty("Grapheme_Extend").getSet("true");
unicodeMap.putAll(graphemeExtend,"Extend");
UnicodeProperty hangul = getProperty("Hangul_Syllable_Type");
unicodeMap.putAll(hangul.getSet("L"),"L");
unicodeMap.putAll(hangul.getSet("V"),"V");
unicodeMap.putAll(hangul.getSet("T"),"T");
unicodeMap.putAll(hangul.getSet("LV"),"LV");
unicodeMap.putAll(hangul.getSet("LVT"),"LVT");
unicodeMap.setMissing("Other");
}
}.setMain("Grapheme_Cluster_Break", "GCB", UnicodeProperty.ENUMERATED, version)
*/
protected UnicodeMap unicodeMap;
public UnicodeMapProperty set(UnicodeMap map) {
unicodeMap = map;
return this;
}
protected String _getValue(int codepoint) {
return (String) unicodeMap.getValue(codepoint);
}
/* protected List _getValueAliases(String valueAlias, List result) {
if (!unicodeMap.getAvailableValues().contains(valueAlias)) return result;
result.add(valueAlias);
return result; // no other aliases
}
*/protected List _getAvailableValues(List result) {
return (List) unicodeMap.getAvailableValues(result);
}
}
public boolean isValidValue(String propertyValue) {
if (isType(STRING_OR_MISC_MASK)) {
return true;
}
Collection<String> values = (Collection<String>) getAvailableValues();
for (String valueAlias : values) {
if (UnicodeProperty.compareNames(valueAlias, propertyValue) == 0) {
return true;
}
for (String valueAlias2 : (Collection<String>) getValueAliases(valueAlias)) {
if (UnicodeProperty.compareNames(valueAlias2, propertyValue) == 0) {
return true;
}
}
}
return false;
}
public List<String> getValueAliases() {
List<String> result = new ArrayList();
if (isType(STRING_OR_MISC_MASK)) {
return result;
}
Collection<String> values = (Collection<String>) getAvailableValues();
for (String valueAlias : values) {
UnicodeProperty.addAllUnique(getValueAliases(valueAlias), result);
}
result.removeAll(values);
return result;
}
public static UnicodeSet addUntested(UnicodeSet result, boolean uniformUnassigned) {
if (!uniformUnassigned) return result;
if (result.contains(UnicodeProperty.SAMPLE_UNASSIGNED)) {
result.addAll(UnicodeProperty.UNASSIGNED);
}
if (result.contains(UnicodeProperty.SAMPLE_PRIVATE_USE)) {
result.addAll(UnicodeProperty.PRIVATE_USE);
}
if (result.contains(UnicodeProperty.SAMPLE_SURROGATE)) {
result.addAll(UnicodeProperty.SURROGATE);
}
return result;
}
public static UnicodeMap addUntested(UnicodeMap result, boolean uniformUnassigned) {
if (!uniformUnassigned) return result;
Object temp;
if (null != (temp = result.get(UnicodeProperty.SAMPLE_UNASSIGNED))) {
result.putAll(UnicodeProperty.UNASSIGNED, temp);
}
if (null != (temp = result.get(UnicodeProperty.SAMPLE_PRIVATE_USE))) {
result.putAll(UnicodeProperty.PRIVATE_USE, temp);
}
if (null != (temp = result.get(UnicodeProperty.SAMPLE_SURROGATE))) {
result.putAll(UnicodeProperty.SURROGATE, temp);
}
return result;
}
public boolean isDefault(int cp) {
String value = getValue(cp);
if (isType(STRING_OR_MISC_MASK)) {
return equals(cp, value);
}
String defaultValue = getValue(SAMPLE_UNASSIGNED);
return defaultValue == null ? value == null : defaultValue.equals(value);
}
public boolean hasUniformUnassigned() {
return hasUniformUnassigned;
}
protected UnicodeProperty setUniformUnassigned(boolean hasUniformUnassigned) {
this.hasUniformUnassigned = hasUniformUnassigned;
return this;
}
}