001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.dev.test.collator;
007:
008: import java.util.Iterator;
009: import java.util.Locale;
010: import java.util.MissingResourceException;
011: import java.util.Vector;
012:
013: import com.ibm.icu.dev.test.ModuleTest;
014: import com.ibm.icu.dev.test.TestFmwk;
015: import com.ibm.icu.dev.test.TestDataModule.DataMap;
016: import com.ibm.icu.impl.LocaleUtility;
017: import com.ibm.icu.impl.Utility;
018: import com.ibm.icu.lang.UCharacter;
019: import com.ibm.icu.text.CollationElementIterator;
020: import com.ibm.icu.text.CollationKey;
021: import com.ibm.icu.text.Collator;
022: import com.ibm.icu.text.RawCollationKey;
023: import com.ibm.icu.text.RuleBasedCollator;
024: import com.ibm.icu.text.UTF16;
025:
026: public class CollationTest extends ModuleTest {
027: // public methods --------------------------------------------------------
028:
029: public static void main(String[] args) throws Exception {
030: new CollationTest().run(args);
031: }
032:
033: public CollationTest() {
034: super ("com/ibm/icu/dev/data/testdata/",
035: "DataDrivenCollationTest");
036: }
037:
038: public void processModules() {
039: for (Iterator iter = t.getSettingsIterator(); iter.hasNext();) {
040: DataMap setting = (DataMap) iter.next();
041: processSetting(setting);
042: }
043: }
044:
045: // package private methods ----------------------------------------------
046:
047: static void doTest(TestFmwk test, RuleBasedCollator col,
048: String source, String target, int result) {
049: doTestVariant(test, col, source, target, result);
050: if (result == -1) {
051: doTestVariant(test, col, target, source, 1);
052: } else if (result == 1) {
053: doTestVariant(test, col, target, source, -1);
054: } else {
055: doTestVariant(test, col, target, source, 0);
056: }
057:
058: CollationElementIterator iter = col
059: .getCollationElementIterator(source);
060: backAndForth(test, iter);
061: iter.setText(target);
062: backAndForth(test, iter);
063: }
064:
065: /**
066: * Return an integer array containing all of the collation orders
067: * returned by calls to next on the specified iterator
068: */
069: static int[] getOrders(CollationElementIterator iter) {
070: int maxSize = 100;
071: int size = 0;
072: int[] orders = new int[maxSize];
073:
074: int order;
075: while ((order = iter.next()) != CollationElementIterator.NULLORDER) {
076: if (size == maxSize) {
077: maxSize *= 2;
078: int[] temp = new int[maxSize];
079: System.arraycopy(orders, 0, temp, 0, size);
080: orders = temp;
081: }
082: orders[size++] = order;
083: }
084:
085: if (maxSize > size) {
086: int[] temp = new int[size];
087: System.arraycopy(orders, 0, temp, 0, size);
088: orders = temp;
089: }
090: return orders;
091: }
092:
093: static void backAndForth(TestFmwk test,
094: CollationElementIterator iter) {
095: // Run through the iterator forwards and stick it into an array
096: iter.reset();
097: int[] orders = getOrders(iter);
098:
099: // Now go through it backwards and make sure we get the same values
100: int index = orders.length;
101: int o;
102:
103: // reset the iterator
104: iter.reset();
105:
106: while ((o = iter.previous()) != CollationElementIterator.NULLORDER) {
107: if (o != orders[--index]) {
108: if (o == 0) {
109: index++;
110: } else {
111: while (index > 0 && orders[index] == 0) {
112: index--;
113: }
114: if (o != orders[index]) {
115: test.errln("Mismatch at index " + index
116: + ": 0x"
117: + Integer.toHexString(orders[index])
118: + " vs 0x" + Integer.toHexString(o));
119: break;
120: }
121: }
122: }
123: }
124:
125: while (index != 0 && orders[index - 1] == 0) {
126: index--;
127: }
128:
129: if (index != 0) {
130: String msg = "Didn't get back to beginning - index is ";
131: test.errln(msg + index);
132:
133: iter.reset();
134: test.err("next: ");
135: while ((o = iter.next()) != CollationElementIterator.NULLORDER) {
136: String hexString = "0x" + Integer.toHexString(o) + " ";
137: test.err(hexString);
138: }
139: test.errln("");
140: test.err("prev: ");
141: while ((o = iter.previous()) != CollationElementIterator.NULLORDER) {
142: String hexString = "0x" + Integer.toHexString(o) + " ";
143: test.err(hexString);
144: }
145: test.errln("");
146: }
147: }
148:
149: // private data members --------------------------------------------------
150:
151: private String m_sequence_;
152: private int m_sequenceIndex_;
153: private String m_source_;
154: private StringBuffer m_target_ = new StringBuffer();
155: private int m_nextRelation_;
156: private int m_relation_;
157:
158: // private methods -------------------------------------------------------
159:
160: private void processSetting(DataMap settings) {
161: RuleBasedCollator col = null;
162: // ok i have to be careful here since it seems like we can have
163: // multiple locales for each test
164: String locale = settings.getString("TestLocale");
165:
166: if (locale != null) {
167: // this is a case where we have locale
168: try {
169: Locale l = LocaleUtility.getLocaleFromName(locale);
170: col = (RuleBasedCollator) Collator.getInstance(l);
171: } catch (MissingResourceException e) {
172: warnln("Could not load the locale data for locale "
173: + locale);
174: } catch (Exception e) {
175: errln("Error creating collator for locale " + locale);
176: }
177: logln("Testing collator for locale " + locale);
178: processSetting2(settings, col);
179: }
180: String rules = settings.getString("Rules");
181: // ok i have to be careful here since it seems like we can have
182: // multiple rules for each test
183: if (rules != null) {
184: // here we deal with rules
185: try {
186: col = new RuleBasedCollator(rules);
187: } catch (MissingResourceException e) {
188: warnln("Could not load the locale data: "
189: + e.getMessage());
190: } catch (Exception e) {
191: errln("Error creating collator for rules " + rules);
192: }
193: processSetting2(settings, col);
194: }
195: }
196:
197: private void processSetting2(DataMap settings, RuleBasedCollator col) {
198:
199: // ok i have to be careful here since it seems like we can have
200: // multiple rules for each test
201: String arguments = settings.getString("Arguments");
202: if (arguments != null) {
203: handleArguments(col, arguments);
204: }
205: processTestCases(col);
206: }
207:
208: /**
209: * Reads the options string and sets appropriate attributes in collator
210: */
211: private void handleArguments(RuleBasedCollator col, String argument) {
212: int i = 0;
213: boolean printInfo = false;
214: while (i < argument.length()) {
215: if (!UCharacter.isWhitespace(argument.charAt(i))) {
216: // eat whitespace
217: break;
218: }
219: i++;
220: }
221: while (i < argument.length()) {
222: // skip opening '['
223: if (argument.charAt(i) == '[') {
224: i++;
225: } else {
226: if (!isModularBuild()) {
227: errln("Error in collation arguments, missing ["); // no opening '['
228: }
229: // !!! following line has no effect
230: printInfo = true;
231: return;
232: }
233:
234: int value = argument.indexOf(' ', i);
235: String option = argument.substring(i, value);
236: i = argument.indexOf(']', value);
237: String optionvalue = argument.substring(value + 1, i);
238: i++;
239: // some options are not added because they have no public apis yet
240: // TODO add the rest of the options
241: if (option.equalsIgnoreCase("alternate")) {
242: if (optionvalue.equalsIgnoreCase("non-ignorable")) {
243: col.setAlternateHandlingShifted(false);
244: } else {
245: col.setAlternateHandlingShifted(true);
246: }
247: } else if (option.equals("strength")) {
248: if (optionvalue.equalsIgnoreCase("1")) {
249: col.setStrength(Collator.PRIMARY);
250: } else if (optionvalue.equalsIgnoreCase("2")) {
251: col.setStrength(Collator.SECONDARY);
252: } else if (optionvalue.equalsIgnoreCase("3")) {
253: col.setStrength(Collator.TERTIARY);
254: } else if (optionvalue.equalsIgnoreCase("4")) {
255: col.setStrength(Collator.QUATERNARY);
256: }
257: }
258: }
259: if (printInfo) {
260: warnln("Could not load the locale data. Skipping...");
261: }
262: // !!! effect is odd, if no modular build, this emits no
263: // message at all. How come? Hmmm. printInfo is never
264: // true if we get here, so this code is never executed.
265: /*
266: if(printInfo == true && isModularBuild()){
267: infoln("Could not load the locale data. Skipping...");
268: }
269: */
270: }
271:
272: private void processTestCases(RuleBasedCollator col) {
273: for (Iterator iter = t.getDataIterator(); iter.hasNext();) {
274: DataMap e1 = (DataMap) iter.next();
275: processSequence(col, e1.getString("sequence"));
276: }
277: }
278:
279: private void processSequence(RuleBasedCollator col, String sequence) {
280: // TODO: have a smarter tester that remembers the sequence and ensures
281: // that the complete sequence is in order. That is why I have made a
282: // constraint in the sequence format.
283: m_sequence_ = sequence;
284: m_sequenceIndex_ = 0;
285: m_nextRelation_ = -1;
286: m_target_.delete(0, m_target_.length());
287: Vector vector = new Vector();
288: int lastsmallerthanindex = -1;
289: getNextInSequence();
290: while (getNextInSequence()) {
291: String target = m_target_.toString();
292: doTest(this , col, m_source_, target, m_relation_);
293: int vsize = vector.size();
294: for (int i = vsize - 1; i >= 0; i--) {
295: String source = (String) vector.elementAt(i);
296: if (i > lastsmallerthanindex) {
297: doTest(this , col, source, target, m_relation_);
298: } else {
299: doTest(this , col, source, target, -1);
300: }
301: }
302: vector.addElement(target);
303: if (m_relation_ < 0) {
304: lastsmallerthanindex = vsize - 1;
305: }
306: }
307: }
308:
309: /**
310: * Parses the sequence to be tested
311: */
312: private boolean getNextInSequence() {
313: if (m_sequenceIndex_ >= m_sequence_.length()) {
314: return false;
315: }
316:
317: boolean quoted = false;
318: boolean quotedsingle = false;
319: boolean done = false;
320: int i = m_sequenceIndex_;
321: int offset = 0;
322: m_source_ = m_target_.toString();
323: m_relation_ = m_nextRelation_;
324: m_target_.delete(0, m_target_.length());
325: while (i < m_sequence_.length() && !done) {
326: int ch = UTF16.charAt(m_sequence_, i);
327: if (UCharacter.isSupplementary(ch)) {
328: i += 2;
329: } else {
330: i++;
331: }
332: if (!quoted) {
333: if (UCharacter.isWhitespace(ch)) {
334: continue;
335: }
336: switch (ch) {
337: case 0x003C: // <
338: m_nextRelation_ = -1;
339: done = true;
340: break;
341: case 0x003D: // =
342: m_nextRelation_ = 0;
343: done = true;
344: break;
345: case 0x003E: // >
346: m_nextRelation_ = 1;
347: done = true;
348: break;
349: case 0x0027: // ' very basic quoting
350: quoted = true;
351: quotedsingle = false;
352: break;
353: case 0x005c: // \ single quote
354: quoted = true;
355: quotedsingle = true;
356: break;
357: default:
358: UTF16.insert(m_target_, offset, ch);
359: if (UCharacter.isSupplementary(ch)) {
360: offset += 2;
361: } else {
362: offset++;
363: }
364: }
365: } else {
366: if (ch == 0x0027) {
367: quoted = false;
368: } else {
369: UTF16.insert(m_target_, offset, ch);
370: if (UCharacter.isSupplementary(ch)) {
371: offset += 2;
372: } else {
373: offset++;
374: }
375: }
376: if (quotedsingle) {
377: quoted = false;
378: }
379: }
380: }
381: if (quoted == true) {
382: errln("Quote in sequence not closed!");
383: return false;
384: }
385:
386: m_sequenceIndex_ = i;
387: return true;
388: }
389:
390: private static void doTestVariant(TestFmwk test,
391: RuleBasedCollator myCollation, String source,
392: String target, int result) {
393: boolean printInfo = false;
394: int compareResult = myCollation.compare(source, target);
395: if (compareResult != result) {
396:
397: // !!! if not mod build, error, else nothing.
398: // warnln if not build, error, else always print warning.
399: // do we need a 'quiet warning?' (err or log). Hmmm,
400: // would it work to have the 'verbose' flag let you
401: // suppress warnings? Are there ever some warnings you
402: // want to suppress, and others you don't?
403: if (!test.isModularBuild()) {
404: test.errln("Comparing \"" + Utility.hex(source)
405: + "\" with \"" + Utility.hex(target)
406: + "\" expected " + result + " but got "
407: + compareResult);
408: } else {
409: printInfo = true;
410: }
411: }
412: CollationKey ssk = myCollation.getCollationKey(source);
413: CollationKey tsk = myCollation.getCollationKey(target);
414: compareResult = ssk.compareTo(tsk);
415: if (compareResult != result) {
416:
417: if (!test.isModularBuild()) {
418: test.errln("Comparing CollationKeys of \""
419: + Utility.hex(source) + "\" with \""
420: + Utility.hex(target) + "\" expected " + result
421: + " but got " + compareResult);
422: } else {
423: printInfo = true;
424: }
425: }
426: RawCollationKey srsk = new RawCollationKey();
427: myCollation.getRawCollationKey(source, srsk);
428: RawCollationKey trsk = new RawCollationKey();
429: myCollation.getRawCollationKey(target, trsk);
430: compareResult = ssk.compareTo(tsk);
431: if (compareResult != result) {
432:
433: if (!test.isModularBuild()) {
434: test.errln("Comparing RawCollationKeys of \""
435: + Utility.hex(source) + "\" with \""
436: + Utility.hex(target) + "\" expected " + result
437: + " but got " + compareResult);
438: } else {
439: printInfo = true;
440: }
441: }
442: // hmmm, but here we issue a warning
443: // only difference is, one warning or two, and detailed info or not?
444: // hmmm, does seem preferable to omit detail if we know it is due to missing resource data.
445: // well, if we label the errors as warnings, we can let people know the details, but
446: // also know they may be due to missing resource data. basically this code is asserting
447: // that the errors are due to missing resource data, which may or may not be true.
448: if (printInfo) {
449: test.warnln("Could not load locale data skipping.");
450: }
451: }
452: }
|