2 //#if defined(FOUNDATION10) || defined(J2SE13)
\r
5 *******************************************************************************
\r
6 * Copyright (C) 2002-2009, International Business Machines Corporation and *
\r
7 * others. All Rights Reserved. *
\r
8 *******************************************************************************
\r
10 package com.ibm.icu.dev.test.util;
\r
12 import java.util.ArrayList;
\r
13 import java.util.Arrays;
\r
14 import java.util.Collection;
\r
15 import java.util.HashMap;
\r
16 import java.util.Iterator;
\r
17 import java.util.List;
\r
18 import java.util.Locale;
\r
19 import java.util.Map;
\r
20 import java.util.Set;
\r
21 import java.util.TreeSet;
\r
23 import com.ibm.icu.lang.UCharacter;
\r
24 import com.ibm.icu.lang.UProperty;
\r
25 import com.ibm.icu.text.Normalizer;
\r
26 import com.ibm.icu.text.UTF16;
\r
27 import com.ibm.icu.util.VersionInfo;
\r
31 * Provides a general interface for Unicode Properties, and
\r
32 * extracting sets based on those values.
\r
36 public class ICUPropertyFactory extends UnicodeProperty.Factory {
\r
38 static class ICUProperty extends UnicodeProperty {
\r
39 protected int propEnum = Integer.MIN_VALUE;
\r
41 protected ICUProperty(String propName, int propEnum) {
\r
43 this.propEnum = propEnum;
\r
44 setType(internalGetPropertyType(propEnum));
\r
47 boolean shownException = false;
\r
49 public String _getValue(int codePoint) {
\r
51 case UProperty.AGE: String temp = UCharacter.getAge(codePoint).toString();
\r
52 if (temp.equals("0.0.0.0")) return "unassigned";
\r
53 if (temp.endsWith(".0.0")) return temp.substring(0,temp.length()-4);
\r
55 case UProperty.BIDI_MIRRORING_GLYPH: return UTF16.valueOf(UCharacter.getMirror(codePoint));
\r
56 case UProperty.CASE_FOLDING: return UCharacter.foldCase(UTF16.valueOf(codePoint),true);
\r
57 case UProperty.ISO_COMMENT: return UCharacter.getISOComment(codePoint);
\r
58 case UProperty.LOWERCASE_MAPPING: return UCharacter.toLowerCase(Locale.ENGLISH,UTF16.valueOf(codePoint));
\r
59 case UProperty.NAME: return UCharacter.getName(codePoint);
\r
60 case UProperty.SIMPLE_CASE_FOLDING: return UTF16.valueOf(UCharacter.foldCase(codePoint,true));
\r
61 case UProperty.SIMPLE_LOWERCASE_MAPPING: return UTF16.valueOf(UCharacter.toLowerCase(codePoint));
\r
62 case UProperty.SIMPLE_TITLECASE_MAPPING: return UTF16.valueOf(UCharacter.toTitleCase(codePoint));
\r
63 case UProperty.SIMPLE_UPPERCASE_MAPPING: return UTF16.valueOf(UCharacter.toUpperCase(codePoint));
\r
64 case UProperty.TITLECASE_MAPPING: return UCharacter.toTitleCase(Locale.ENGLISH,UTF16.valueOf(codePoint),null);
\r
65 case UProperty.UNICODE_1_NAME: return UCharacter.getName1_0(codePoint);
\r
66 case UProperty.UPPERCASE_MAPPING: return UCharacter.toUpperCase(Locale.ENGLISH,UTF16.valueOf(codePoint));
\r
67 case NFC: return Normalizer.normalize(codePoint, Normalizer.NFC);
\r
68 case NFD: return Normalizer.normalize(codePoint, Normalizer.NFD);
\r
69 case NFKC: return Normalizer.normalize(codePoint, Normalizer.NFKC);
\r
70 case NFKD: return Normalizer.normalize(codePoint, Normalizer.NFKD);
\r
71 case isNFC: return String.valueOf(Normalizer.normalize(codePoint, Normalizer.NFC).equals(UTF16.valueOf(codePoint)));
\r
72 case isNFD: return String.valueOf(Normalizer.normalize(codePoint, Normalizer.NFD).equals(UTF16.valueOf(codePoint)));
\r
73 case isNFKC: return String.valueOf(Normalizer.normalize(codePoint, Normalizer.NFKC).equals(UTF16.valueOf(codePoint)));
\r
74 case isNFKD: return String.valueOf(Normalizer.normalize(codePoint, Normalizer.NFKD).equals(UTF16.valueOf(codePoint)));
\r
75 case isLowercase: return String.valueOf(UCharacter.toLowerCase(Locale.ENGLISH,UTF16.valueOf(codePoint)).equals(UTF16.valueOf(codePoint)));
\r
76 case isUppercase: return String.valueOf(UCharacter.toUpperCase(Locale.ENGLISH,UTF16.valueOf(codePoint)).equals(UTF16.valueOf(codePoint)));
\r
77 case isTitlecase: return String.valueOf(UCharacter.toTitleCase(Locale.ENGLISH,UTF16.valueOf(codePoint),null).equals(UTF16.valueOf(codePoint)));
\r
78 case isCasefolded: return String.valueOf(UCharacter.foldCase(UTF16.valueOf(codePoint),true).equals(UTF16.valueOf(codePoint)));
\r
79 case isCased: return String.valueOf(UCharacter.toLowerCase(Locale.ENGLISH,UTF16.valueOf(codePoint)).equals(UTF16.valueOf(codePoint)));
\r
81 if (propEnum < UProperty.INT_LIMIT) {
\r
83 String value = null;
\r
85 enumValue = UCharacter.getIntPropertyValue(codePoint, propEnum);
\r
86 if (enumValue >= 0) value = fixedGetPropertyValueName(propEnum,enumValue, UProperty.NameChoice.LONG);
\r
87 } catch (IllegalArgumentException e) {
\r
88 if (!shownException) {
\r
89 System.out.println("Fail: " + getName() + ", " + Integer.toHexString(codePoint));
\r
90 shownException = true;
\r
93 return value != null ? value : String.valueOf(enumValue);
\r
94 } else if (propEnum < UProperty.DOUBLE_LIMIT) {
\r
95 double num = UCharacter.getUnicodeNumericValue(codePoint);
\r
96 if (num == UCharacter.NO_NUMERIC_VALUE) return null;
\r
97 return Double.toString(num);
\r
98 // TODO: Fix HACK -- API deficient
\r
104 * @param valueAlias null if unused.
\r
105 * @param valueEnum -1 if unused
\r
106 * @param nameChoice
\r
109 private String getFixedValueAlias(String valueAlias, int valueEnum, int nameChoice) {
\r
110 if (propEnum >= UProperty.STRING_START) {
\r
111 if (nameChoice != UProperty.NameChoice.LONG) return null;
\r
113 } else if (propEnum >= UProperty.DOUBLE_START) {
\r
114 if (nameChoice != UProperty.NameChoice.LONG) return null;
\r
117 if (valueAlias != null && !valueAlias.equals("<integer>")) {
\r
118 valueEnum = fixedGetPropertyValueEnum(propEnum,valueAlias);
\r
120 // because these are defined badly, there may be no normal (long) name.
\r
122 String result = fixedGetPropertyValueName(propEnum, valueEnum, nameChoice);
\r
123 if (result != null) return result;
\r
124 // HACK try other namechoice
\r
125 if (nameChoice == UProperty.NameChoice.LONG) {
\r
126 result = fixedGetPropertyValueName(propEnum,valueEnum, UProperty.NameChoice.SHORT);
\r
127 if (result != null) return result;
\r
128 if (propEnum == UProperty.CANONICAL_COMBINING_CLASS) return null;
\r
129 return "<integer>";
\r
134 private static int fixedGetPropertyValueEnum(int propEnum, String valueAlias) {
\r
136 return UCharacter.getPropertyValueEnum(propEnum, valueAlias);
\r
137 } catch (Exception e) {
\r
138 return Integer.parseInt(valueAlias);
\r
142 static Map fixSkeleton = new HashMap();
\r
143 private static String fixedGetPropertyValueName(int propEnum, int valueEnum, int nameChoice) {
\r
146 String value = UCharacter.getPropertyValueName(propEnum,valueEnum,nameChoice);
\r
147 String newValue = (String) fixSkeleton.get(value);
\r
148 if (newValue == null) {
\r
150 if (propEnum == UProperty.JOINING_GROUP) {
\r
151 newValue = newValue.toLowerCase(Locale.ENGLISH);
\r
153 newValue = regularize(newValue, true);
\r
154 fixSkeleton.put(value, newValue);
\r
157 } catch (Exception e) {
\r
162 public List _getNameAliases(List result) {
\r
163 if (result == null) result = new ArrayList();
\r
164 String alias = String_Extras.get(propEnum);
\r
165 if (alias == null) alias = Binary_Extras.get(propEnum);
\r
166 if (alias != null) {
\r
167 addUnique(alias, result);
\r
169 addUnique(getFixedPropertyName(propEnum, UProperty.NameChoice.SHORT), result);
\r
170 addUnique(getFixedPropertyName(propEnum, UProperty.NameChoice.LONG), result);
\r
175 public String getFixedPropertyName(int propName, int nameChoice) {
\r
177 return UCharacter.getPropertyName(propEnum, nameChoice);
\r
178 } catch (IllegalArgumentException e) {
\r
183 private Map cccHack = new HashMap();
\r
184 boolean needCccHack = true;
\r
186 public List _getAvailableValues(List result) {
\r
187 if (result == null) result = new ArrayList();
\r
188 if (propEnum == UProperty.AGE) {
\r
189 addAllUnique(getAges(),
\r
193 if (propEnum < UProperty.INT_LIMIT) {
\r
194 if (Binary_Extras.isInRange(propEnum)) {
\r
195 propEnum = UProperty.BINARY_START; // HACK
\r
197 int start = UCharacter.getIntPropertyMinValue(propEnum);
\r
198 int end = UCharacter.getIntPropertyMaxValue(propEnum);
\r
199 for (int i = start; i <= end; ++i) {
\r
200 String alias = getFixedValueAlias(null, i, UProperty.NameChoice.LONG);
\r
201 String alias2 = getFixedValueAlias(null, i, UProperty.NameChoice.SHORT);
\r
202 if (alias == null) {
\r
204 if (alias == null && propEnum == UProperty.CANONICAL_COMBINING_CLASS) {
\r
205 alias = String.valueOf(i);
\r
208 if (needCccHack && propEnum == UProperty.CANONICAL_COMBINING_CLASS) { // HACK
\r
209 cccHack.put(alias, String.valueOf(i));
\r
211 //System.out.println(propertyAlias + "\t" + i + ":\t" + alias);
\r
212 addUnique(alias, result);
\r
214 needCccHack = false;
\r
216 String alias = getFixedValueAlias(null, -1,UProperty.NameChoice.LONG);
\r
217 addUnique(alias, result);
\r
222 static String[] AGES = null;
\r
223 private String[] getAges() {
\r
224 if (AGES == null) {
\r
225 Set ages = new TreeSet();
\r
226 for (int i = 0; i < 0x10FFFF; ++i) {
\r
227 VersionInfo age = UCharacter.getAge(i);
\r
228 ages.add(age.toString());
\r
230 AGES = (String[]) ages.toArray(new String[ages.size()]);
\r
235 public List _getValueAliases(String valueAlias, List result) {
\r
236 if (result == null) result = new ArrayList();
\r
237 if (propEnum == UProperty.AGE) {
\r
238 addUnique(valueAlias, result);
\r
241 if (propEnum == UProperty.CANONICAL_COMBINING_CLASS) {
\r
242 addUnique(cccHack.get(valueAlias), result); // add number
\r
244 addUnique(getFixedValueAlias(valueAlias, -1, UProperty.NameChoice.SHORT), result);
\r
245 addUnique(getFixedValueAlias(valueAlias, -1, UProperty.NameChoice.LONG), result);
\r
251 * @see com.ibm.icu.dev.test.util.UnicodePropertySource#getPropertyType()
\r
253 private int internalGetPropertyType(int prop) {
\r
255 case UProperty.AGE:
\r
256 case UProperty.BLOCK:
\r
257 case UProperty.SCRIPT:
\r
258 return UnicodeProperty.CATALOG;
\r
259 case UProperty.ISO_COMMENT:
\r
260 case UProperty.NAME:
\r
261 case UProperty.UNICODE_1_NAME:
\r
262 return UnicodeProperty.MISC;
\r
263 case UProperty.BIDI_MIRRORING_GLYPH:
\r
264 case UProperty.CASE_FOLDING:
\r
265 case UProperty.LOWERCASE_MAPPING:
\r
266 case UProperty.SIMPLE_CASE_FOLDING:
\r
267 case UProperty.SIMPLE_LOWERCASE_MAPPING:
\r
268 case UProperty.SIMPLE_TITLECASE_MAPPING:
\r
269 case UProperty.SIMPLE_UPPERCASE_MAPPING:
\r
270 case UProperty.TITLECASE_MAPPING:
\r
271 case UProperty.UPPERCASE_MAPPING:
\r
272 return UnicodeProperty.EXTENDED_STRING;
\r
274 if (prop < UProperty.BINARY_START) return UnicodeProperty.UNKNOWN;
\r
275 if (prop < UProperty.BINARY_LIMIT) return UnicodeProperty.BINARY;
\r
276 if (prop < UProperty.INT_START) return UnicodeProperty.EXTENDED_BINARY;
\r
277 if (prop < UProperty.INT_LIMIT) return UnicodeProperty.ENUMERATED;
\r
278 if (prop < UProperty.DOUBLE_START) return UnicodeProperty.EXTENDED_ENUMERATED;
\r
279 if (prop < UProperty.DOUBLE_LIMIT) return UnicodeProperty.NUMERIC;
\r
280 if (prop < UProperty.STRING_START) return UnicodeProperty.EXTENDED_NUMERIC;
\r
281 if (prop < UProperty.STRING_LIMIT) return UnicodeProperty.STRING;
\r
282 return UnicodeProperty.EXTENDED_STRING;
\r
286 * @see com.ibm.icu.dev.test.util.UnicodeProperty#getVersion()
\r
288 public String _getVersion() {
\r
289 return VersionInfo.ICU_VERSION.toString();
\r
294 matchIterator = new UnicodeSetIterator(
\r
295 new UnicodeSet("[^[:Cn:]-[:Default_Ignorable_Code_Point:]]"));
\r
301 * Other Missing Functions:
\r
306 Composition_Exclusion
\r
307 Decomposition_Mapping
\r
314 Special_Case_Condition
\r
315 Unicode_Radical_Stroke
\r
318 static final Names Binary_Extras = new Names(UProperty.BINARY_LIMIT,
\r
320 "isNFC", "isNFD", "isNFKC", "isNFKD",
\r
321 "isLowercase", "isUppercase", "isTitlecase", "isCasefolded", "isCased",
\r
324 static final Names String_Extras = new Names(UProperty.STRING_LIMIT,
\r
326 "toNFC", "toNFD", "toNFKC", "toNKFD",
\r
330 isNFC = UProperty.BINARY_LIMIT,
\r
331 isNFD = UProperty.BINARY_LIMIT+1,
\r
332 isNFKC = UProperty.BINARY_LIMIT+2,
\r
333 isNFKD = UProperty.BINARY_LIMIT+3,
\r
334 isLowercase = UProperty.BINARY_LIMIT+4,
\r
335 isUppercase = UProperty.BINARY_LIMIT+5,
\r
336 isTitlecase = UProperty.BINARY_LIMIT+6,
\r
337 isCasefolded = UProperty.BINARY_LIMIT+7,
\r
338 isCased = UProperty.BINARY_LIMIT+8,
\r
340 NFC = UProperty.STRING_LIMIT,
\r
341 NFD = UProperty.STRING_LIMIT+1,
\r
342 NFKC = UProperty.STRING_LIMIT+2,
\r
343 NFKD = UProperty.STRING_LIMIT+3
\r
346 private ICUPropertyFactory() {
\r
347 Collection c = getInternalAvailablePropertyAliases(new ArrayList());
\r
348 Iterator it = c.iterator();
\r
349 while (it.hasNext()) {
\r
350 add(getInternalProperty((String)it.next()));
\r
354 private static ICUPropertyFactory singleton = null;
\r
356 public static synchronized ICUPropertyFactory make() {
\r
357 if (singleton != null) return singleton;
\r
358 singleton = new ICUPropertyFactory();
\r
362 public List getInternalAvailablePropertyAliases(List result) {
\r
364 {UProperty.BINARY_START, UProperty.BINARY_LIMIT},
\r
365 {UProperty.INT_START, UProperty.INT_LIMIT},
\r
366 {UProperty.DOUBLE_START, UProperty.DOUBLE_LIMIT},
\r
367 {UProperty.STRING_START, UProperty.STRING_LIMIT},
\r
369 for (int i = 0; i < ranges.length; ++i) {
\r
370 for (int j = ranges[i][0]; j < ranges[i][1]; ++j) {
\r
371 String alias = UCharacter.getPropertyName(j, UProperty.NameChoice.LONG);
\r
372 UnicodeProperty.addUnique(alias, result);
\r
373 if (!result.contains(alias)) result.add(alias);
\r
376 result.addAll(String_Extras.getNames());
\r
377 result.addAll(Binary_Extras.getNames());
\r
381 public UnicodeProperty getInternalProperty(String propertyAlias) {
\r
385 int possibleItem = Binary_Extras.get(propertyAlias);
\r
386 if (possibleItem >= 0) {
\r
387 propEnum = possibleItem;
\r
390 possibleItem = String_Extras.get(propertyAlias);
\r
391 if (possibleItem >= 0) {
\r
392 propEnum = possibleItem;
\r
395 propEnum = UCharacter.getPropertyEnum(propertyAlias);
\r
397 return new ICUProperty(propertyAlias, propEnum);
\r
401 * @see com.ibm.icu.dev.test.util.UnicodePropertySource#getProperty(java.lang.String)
\r
403 // TODO file bug on getPropertyValueName for Canonical_Combining_Class
\r
405 public static class Names {
\r
406 private String[] names;
\r
408 public Names(int base, String[] names) {
\r
410 this.names = names;
\r
412 public int get(String name) {
\r
413 for (int i = 0; i < names.length; ++i) {
\r
414 if (name.equalsIgnoreCase(names[i])) return base + i;
\r
418 public String get(int number) {
\r
420 if (number < 0 || names.length <= number) return null;
\r
421 return names[number];
\r
423 public boolean isInRange(int number) {
\r
425 return (0 <= number && number < names.length);
\r
427 public List getNames() {
\r
428 return Arrays.asList(names);
\r