2 **********************************************************************
\r
3 * Copyright (c) 2006-2009, International Business Machines
\r
4 * Corporation and others. All Rights Reserved.
\r
5 **********************************************************************
\r
6 * Created on 2006-4-21
\r
8 package com.ibm.icu.dev.test;
\r
10 import java.util.Arrays;
\r
11 import java.util.HashMap;
\r
12 import java.util.Iterator;
\r
13 import java.util.Map;
\r
14 import java.util.MissingResourceException;
\r
15 import java.util.NoSuchElementException;
\r
17 import com.ibm.icu.impl.ICUResourceBundle;
\r
18 import com.ibm.icu.util.UResourceBundle;
\r
19 import com.ibm.icu.util.UResourceBundleIterator;
\r
20 import com.ibm.icu.util.UResourceTypeMismatchException;
\r
23 * Represents a collection of test data described in a UResourceBoundle file.
\r
25 * The root of the UResourceBoundle file is a table resource, and it has one
\r
26 * Info and one TestData sub-resources. The Info describes the data module
\r
27 * itself. The TestData, which is a table resource, has a collection of test
\r
30 * The test data is a named table resource which has Info, Settings, Headers,
\r
31 * and Cases sub-resources.
\r
34 * DataModule:table(nofallback){
\r
47 * The test data is expected to be fed to test code by following sequence
\r
49 * for each setting in Setting{
\r
50 * prepare the setting
\r
51 * for each test data in Cases{
\r
56 * For detail of the specification, please refer to the code. The code is
\r
57 * initially ported from "icu4c/source/tools/ctestfw/unicode/tstdtmod.h"
\r
58 * and should be maintained parallelly.
\r
60 * @author Raymond Yang
\r
62 class ResourceModule implements TestDataModule {
\r
63 private static final String INFO = "Info";
\r
64 // private static final String DESCRIPTION = "Description";
\r
65 // private static final String LONG_DESCRIPTION = "LongDescription";
\r
66 private static final String TEST_DATA = "TestData";
\r
67 private static final String SETTINGS = "Settings";
\r
68 private static final String HEADER = "Headers";
\r
69 private static final String DATA = "Cases";
\r
72 UResourceBundle res;
\r
73 UResourceBundle info;
\r
74 UResourceBundle defaultHeader;
\r
75 UResourceBundle testData;
\r
77 ResourceModule(String baseName, String localeName) throws DataModuleFormatError{
\r
79 res = (UResourceBundle) UResourceBundle.getBundleInstance(baseName, localeName);
\r
80 info = getFromTable(res, INFO, UResourceBundle.TABLE);
\r
81 testData = getFromTable(res, TEST_DATA, UResourceBundle.TABLE);
\r
84 // unfortunately, actually, data can be either ARRAY or STRING
\r
85 defaultHeader = getFromTable(info, HEADER, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});
\r
86 } catch (MissingResourceException e){
\r
87 defaultHeader = null;
\r
91 public String getName() {
\r
92 return res.getKey();
\r
95 public DataMap getInfo() {
\r
96 return new UTableResource(info);
\r
99 public TestData getTestData(String testName) throws DataModuleFormatError {
\r
100 return new UResourceTestData(defaultHeader, testData.get(testName));
\r
103 public Iterator getTestDataIterator() {
\r
104 return new IteratorAdapter(testData){
\r
105 protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {
\r
106 return new UResourceTestData(defaultHeader, nextRes);
\r
112 * To make UResourceBundleIterator works like Iterator
\r
113 * and return various data-driven test object for next() call
\r
115 * @author Raymond Yang
\r
117 private abstract static class IteratorAdapter implements Iterator{
\r
118 private UResourceBundle res;
\r
119 private UResourceBundleIterator itr;
\r
120 private Object preparedNextElement = null;
\r
121 // fix a strange behavior for UResourceBundleIterator for
\r
122 // UResourceBundle.STRING. It support hasNext(), but does
\r
123 // not support next() now.
\r
125 // Use the iterated resource itself as the result from next() call
\r
126 private boolean isStrRes = false;
\r
127 private boolean isStrResPrepared = false; // for STRING resouce, we only prepare once
\r
129 IteratorAdapter(UResourceBundle theRes) {
\r
130 assert_not (theRes == null);
\r
132 itr = ((ICUResourceBundle)res).getIterator();
\r
133 isStrRes = res.getType() == UResourceBundle.STRING;
\r
136 public void remove() {
\r
140 private boolean hasNextForStrRes(){
\r
141 assert_is (isStrRes);
\r
142 assert_not (!isStrResPrepared && preparedNextElement != null);
\r
143 if (isStrResPrepared && preparedNextElement != null) return true;
\r
144 if (isStrResPrepared && preparedNextElement == null) return false; // only prepare once
\r
145 assert_is (!isStrResPrepared && preparedNextElement == null);
\r
148 preparedNextElement = prepareNext(res);
\r
149 assert_not (preparedNextElement == null, "prepareNext() should not return null");
\r
150 isStrResPrepared = true; // toggle the tag
\r
152 } catch (DataModuleFormatError e) {
\r
153 throw new RuntimeException(e.getMessage(),e);
\r
156 public boolean hasNext() {
\r
157 if (isStrRes) return hasNextForStrRes();
\r
159 if (preparedNextElement != null) return true;
\r
160 UResourceBundle t = null;
\r
161 if (itr.hasNext()) {
\r
162 // Notice, other RuntimeException may be throwed
\r
169 preparedNextElement = prepareNext(t);
\r
170 assert_not (preparedNextElement == null, "prepareNext() should not return null");
\r
172 } catch (DataModuleFormatError e) {
\r
173 // Sadly, we throw RuntimeException also
\r
174 throw new RuntimeException(e.getMessage(),e);
\r
178 public Object next(){
\r
180 Object t = preparedNextElement;
\r
181 preparedNextElement = null;
\r
184 throw new NoSuchElementException();
\r
188 * To prepare data-driven test object for next() call, should not return null
\r
190 abstract protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError;
\r
195 * Avoid use Java 1.4 language new assert keyword
\r
197 static void assert_is(boolean eq, String msg){
\r
198 if (!eq) throw new Error("test code itself has error: " + msg);
\r
200 static void assert_is(boolean eq){
\r
201 if (!eq) throw new Error("test code itself has error.");
\r
203 static void assert_not(boolean eq, String msg){
\r
204 assert_is(!eq, msg);
\r
206 static void assert_not(boolean eq){
\r
211 * Internal helper function to get resource with following add-on
\r
213 * 1. Assert the returned resource is never null.
\r
214 * 2. Check the type of resource.
\r
216 * The UResourceTypeMismatchException for various get() method is a
\r
217 * RuntimeException which can be silently bypassed. This behavior is a
\r
218 * trouble. One purpose of the class is to enforce format checking for
\r
219 * resource file. We don't want to the exceptions are silently bypassed
\r
220 * and spreaded to our customer's code.
\r
222 * Notice, the MissingResourceException for get() method is also a
\r
223 * RuntimeException. The caller functions should avoid sepread the execption
\r
224 * silently also. The behavior is modified because some resource are
\r
225 * optional and can be missed.
\r
227 static UResourceBundle getFromTable(UResourceBundle res, String key, int expResType) throws DataModuleFormatError{
\r
228 return getFromTable(res, key, new int[]{expResType});
\r
231 static UResourceBundle getFromTable(UResourceBundle res, String key, int[] expResTypes) throws DataModuleFormatError{
\r
232 assert_is (res != null && key != null && res.getType() == UResourceBundle.TABLE);
\r
233 UResourceBundle t = res.get(key);
\r
235 assert_not (t ==null);
\r
236 int type = t.getType();
\r
237 Arrays.sort(expResTypes);
\r
238 if (Arrays.binarySearch(expResTypes, type) >= 0) {
\r
241 throw new DataModuleFormatError(new UResourceTypeMismatchException("Actual type " + t.getType() + " != expected types " + expResTypes + "."));
\r
246 * Unfortunately, UResourceBundle is unable to treat one string as string array.
\r
247 * This function return a String[] from UResourceBundle, regardless it is an array or a string
\r
249 static String[] getStringArrayHelper(UResourceBundle res, String key) throws DataModuleFormatError{
\r
250 UResourceBundle t = getFromTable(res, key, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});
\r
251 return getStringArrayHelper(t);
\r
254 static String[] getStringArrayHelper(UResourceBundle res) throws DataModuleFormatError{
\r
256 int type = res.getType();
\r
258 case UResourceBundle.ARRAY:
\r
259 return res.getStringArray();
\r
260 case UResourceBundle.STRING:
\r
261 return new String[]{res.getString()};
\r
263 throw new UResourceTypeMismatchException("Only accept ARRAY and STRING types.");
\r
265 } catch (UResourceTypeMismatchException e){
\r
266 throw new DataModuleFormatError(e);
\r
270 public static void main(String[] args){
\r
272 TestDataModule m = new ResourceModule("com/ibm/icu/dev/data/testdata/","DataDrivenCollationTest");
\r
273 System.out.println("hello: " + m.getName());
\r
275 m.getTestDataIterator();
\r
276 } catch (DataModuleFormatError e) {
\r
277 // TODO Auto-generated catch block
\r
278 System.out.println("???");
\r
279 e.printStackTrace();
\r
283 private static class UResourceTestData implements TestData{
\r
284 private UResourceBundle res;
\r
285 private UResourceBundle info;
\r
286 private UResourceBundle settings;
\r
287 private UResourceBundle header;
\r
288 private UResourceBundle data;
\r
290 UResourceTestData(UResourceBundle defaultHeader, UResourceBundle theRes) throws DataModuleFormatError{
\r
292 assert_is (theRes != null && theRes.getType() == UResourceBundle.TABLE);
\r
294 // unfortunately, actually, data can be either ARRAY or STRING
\r
295 data = getFromTable(res, DATA, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});
\r
300 // unfortunately, actually, data can be either ARRAY or STRING
\r
301 header = getFromTable(res, HEADER, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});
\r
302 } catch (MissingResourceException e){
\r
303 if (defaultHeader == null) {
\r
304 throw new DataModuleFormatError("Unable to find a header for test data '" + res.getKey() + "' and no default header exist.");
\r
306 header = defaultHeader;
\r
310 settings = getFromTable(res, SETTINGS, UResourceBundle.ARRAY);
\r
311 info = getFromTable(res, INFO, UResourceBundle.TABLE);
\r
312 } catch (MissingResourceException e){
\r
313 // do nothing, left them null;
\r
318 public String getName() {
\r
319 return res.getKey();
\r
322 public DataMap getInfo() {
\r
323 return info == null ? null : new UTableResource(info);
\r
326 public Iterator getSettingsIterator() {
\r
327 assert_is (settings.getType() == UResourceBundle.ARRAY);
\r
328 return new IteratorAdapter(settings){
\r
329 protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {
\r
330 return new UTableResource(nextRes);
\r
335 public Iterator getDataIterator() {
\r
337 assert_is (data.getType() == UResourceBundle.ARRAY
\r
338 || data.getType() == UResourceBundle.STRING);
\r
339 return new IteratorAdapter(data){
\r
340 protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {
\r
341 return new UArrayResource(header, nextRes);
\r
347 private static class UTableResource implements DataMap{
\r
348 private UResourceBundle res;
\r
350 UTableResource(UResourceBundle theRes){
\r
353 public String getString(String key) {
\r
356 t = res.getString(key);
\r
357 } catch (MissingResourceException e){
\r
362 public Object getObject(String key) {
\r
364 return res.get(key);
\r
368 private static class UArrayResource implements DataMap{
\r
369 private Map theMap;
\r
370 UArrayResource(UResourceBundle theHeader, UResourceBundle theData) throws DataModuleFormatError{
\r
371 assert_is (theHeader != null && theData != null);
\r
374 header = getStringArrayHelper(theHeader);
\r
375 if (theData.getSize() != header.length)
\r
376 throw new DataModuleFormatError("The count of Header and Data is mismatch.");
\r
377 theMap = new HashMap();
\r
378 for (int i = 0; i < header.length; i++) {
\r
379 if(theData.getType()==UResourceBundle.ARRAY){
\r
380 theMap.put(header[i], theData.get(i));
\r
381 }else if(theData.getType()==UResourceBundle.STRING){
\r
382 theMap.put(header[i], theData.getString());
\r
384 throw new DataModuleFormatError("Did not get the expected data!");
\r
390 public String getString(String key) {
\r
391 Object o = theMap.get(key);
\r
392 UResourceBundle rb;
\r
393 if(o instanceof UResourceBundle) {
\r
394 // unpack ResourceBundle strings
\r
395 rb = (UResourceBundle)o;
\r
396 return rb.getString();
\r
400 public Object getObject(String key) {
\r
401 return theMap.get(key);
\r