]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/framework/src/com/ibm/icu/dev/test/ResourceModule.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / framework / src / com / ibm / icu / dev / test / ResourceModule.java
1 /*\r
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
7  */\r
8 package com.ibm.icu.dev.test;\r
9 \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
16 \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
21 \r
22 /**\r
23  * Represents a collection of test data described in a UResourceBoundle file. \r
24  * \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
28  * data.\r
29  * \r
30  * The test data is a named table resource which has Info, Settings, Headers,\r
31  * and Cases sub-resources. \r
32  * \r
33  * <pre>\r
34  * DataModule:table(nofallback){ \r
35  *   Info:table {} \r
36  *   TestData:table {\r
37  *     entry_name:table{\r
38  *       Info:table{}\r
39  *       Settings:array{}\r
40  *       Headers:array{}\r
41  *       Cases:array{}\r
42  *     }\r
43  *   } \r
44  * }\r
45  * </pre>\r
46  * \r
47  * The test data is expected to be fed to test code by following sequence \r
48  *\r
49  *   for each setting in Setting{\r
50  *       prepare the setting\r
51  *     for each test data in Cases{\r
52  *       perform the test\r
53  *     }\r
54  *   }\r
55  * \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
59  * \r
60  * @author Raymond Yang\r
61  */\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
70 \r
71     \r
72     UResourceBundle res;\r
73     UResourceBundle info;\r
74     UResourceBundle defaultHeader;\r
75     UResourceBundle testData;\r
76     \r
77     ResourceModule(String baseName, String localeName) throws DataModuleFormatError{\r
78 \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
82 \r
83         try {\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
88         }\r
89     }\r
90 \r
91     public String getName() {\r
92         return res.getKey();\r
93     }\r
94 \r
95     public DataMap getInfo() {\r
96         return new UTableResource(info);\r
97     }\r
98 \r
99     public TestData getTestData(String testName) throws DataModuleFormatError {\r
100         return new UResourceTestData(defaultHeader, testData.get(testName));\r
101     }\r
102 \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
107             }\r
108         };\r
109     }\r
110 \r
111     /**\r
112      * To make UResourceBundleIterator works like Iterator\r
113      * and return various data-driven test object for next() call\r
114      * \r
115      * @author Raymond Yang\r
116      */\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
124         // \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
128 \r
129         IteratorAdapter(UResourceBundle theRes) {\r
130             assert_not (theRes == null);\r
131             res = theRes;\r
132             itr = ((ICUResourceBundle)res).getIterator();\r
133             isStrRes = res.getType() == UResourceBundle.STRING;\r
134         }\r
135         \r
136         public void remove() {\r
137             // do nothing\r
138         }\r
139 \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
146             \r
147             try {\r
148                 preparedNextElement = prepareNext(res);\r
149                 assert_not (preparedNextElement == null, "prepareNext() should not return null");\r
150                 isStrResPrepared = true; // toggle the tag\r
151                 return true;\r
152             } catch (DataModuleFormatError e) {\r
153                 throw new RuntimeException(e.getMessage(),e);\r
154             }            \r
155         }\r
156         public boolean hasNext() {\r
157             if (isStrRes) return hasNextForStrRes();\r
158             \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
163                 t = itr.next();\r
164             } else {\r
165                 return false;\r
166             }\r
167 \r
168             try {\r
169                 preparedNextElement = prepareNext(t);\r
170                 assert_not (preparedNextElement == null, "prepareNext() should not return null");\r
171                 return true;\r
172             } catch (DataModuleFormatError e) {\r
173                 // Sadly, we throw RuntimeException also\r
174                 throw new RuntimeException(e.getMessage(),e);\r
175             }\r
176         }\r
177 \r
178         public Object next(){\r
179             if (hasNext()) {\r
180                 Object t = preparedNextElement;\r
181                 preparedNextElement = null;\r
182                 return t;\r
183             } else {\r
184                 throw new NoSuchElementException();\r
185             }\r
186         }\r
187         /**\r
188          * To prepare data-driven test object for next() call, should not return null\r
189          */\r
190         abstract protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError;\r
191     }\r
192     \r
193     \r
194     /**\r
195      * Avoid use Java 1.4 language new assert keyword \r
196      */\r
197     static void assert_is(boolean eq, String msg){\r
198         if (!eq) throw new Error("test code itself has error: " + msg);\r
199     }\r
200     static void assert_is(boolean eq){\r
201         if (!eq) throw new Error("test code itself has error.");\r
202     }\r
203     static void assert_not(boolean eq, String msg){\r
204         assert_is(!eq, msg);\r
205     }\r
206     static void assert_not(boolean eq){\r
207         assert_is(!eq);\r
208     }\r
209             \r
210     /**\r
211      * Internal helper function to get resource with following add-on \r
212      * \r
213      * 1. Assert the returned resource is never null.\r
214      * 2. Check the type of resource. \r
215      * \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
221      * \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
226      */\r
227     static UResourceBundle getFromTable(UResourceBundle res, String key, int expResType) throws DataModuleFormatError{\r
228         return getFromTable(res, key, new int[]{expResType});\r
229     }\r
230     \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
234       \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
239             return t;\r
240         } else {\r
241             throw new DataModuleFormatError(new UResourceTypeMismatchException("Actual type " + t.getType() + " != expected types " + expResTypes + "."));\r
242         }\r
243     }\r
244     \r
245     /**\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
248      */\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
252     }\r
253 \r
254     static String[] getStringArrayHelper(UResourceBundle res) throws DataModuleFormatError{\r
255         try{\r
256             int type = res.getType();\r
257             switch (type) {\r
258             case UResourceBundle.ARRAY:\r
259                 return res.getStringArray();\r
260             case UResourceBundle.STRING:\r
261                 return new String[]{res.getString()};\r
262             default:\r
263                 throw new UResourceTypeMismatchException("Only accept ARRAY and STRING types.");\r
264             }\r
265         } catch (UResourceTypeMismatchException e){\r
266             throw new DataModuleFormatError(e);\r
267         }\r
268     }\r
269     \r
270     public static void main(String[] args){\r
271         try {\r
272             TestDataModule m = new ResourceModule("com/ibm/icu/dev/data/testdata/","DataDrivenCollationTest");\r
273         System.out.println("hello: " + m.getName());\r
274         m.getInfo();\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
280         }\r
281     }\r
282 \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
289 \r
290         UResourceTestData(UResourceBundle defaultHeader, UResourceBundle theRes) throws DataModuleFormatError{\r
291             \r
292             assert_is (theRes != null && theRes.getType() == UResourceBundle.TABLE);\r
293             res = theRes;\r
294             // unfortunately, actually, data can be either ARRAY or STRING\r
295             data = getFromTable(res, DATA, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});\r
296        \r
297 \r
298             \r
299             try {\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
305                 } else {\r
306                     header = defaultHeader;\r
307                 }\r
308             }\r
309          try{\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
314                 settings = data;\r
315             }\r
316         }\r
317         \r
318         public String getName() {\r
319             return res.getKey();\r
320         }\r
321 \r
322         public DataMap getInfo() {\r
323             return info == null ? null : new UTableResource(info);\r
324         }\r
325 \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
331                 }\r
332             };\r
333         }\r
334 \r
335         public Iterator getDataIterator() {\r
336             // unfortunately,\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
342                 }\r
343             };\r
344         }\r
345     }\r
346         \r
347     private static class UTableResource implements DataMap{\r
348         private UResourceBundle res;\r
349 \r
350         UTableResource(UResourceBundle theRes){\r
351             res = theRes;\r
352         }\r
353         public String getString(String key) {\r
354             String t;\r
355             try{\r
356                 t = res.getString(key);\r
357             } catch (MissingResourceException e){\r
358                 t = null;\r
359             }\r
360             return t;\r
361         }\r
362          public Object getObject(String key) {\r
363             \r
364             return res.get(key);\r
365         }\r
366     }\r
367     \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
372             String[] header;\r
373          \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
383                 }else{\r
384                     throw new DataModuleFormatError("Did not get the expected data!");                   \r
385                 }\r
386             }\r
387             \r
388         }\r
389         \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
397             }\r
398             return (String)o;\r
399         }\r
400         public Object getObject(String key) {\r
401             return theMap.get(key);\r
402         }\r
403     }\r
404 }\r