]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/test/ResourceModule.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / test / ResourceModule.java
1 //##header\r
2 /*\r
3  **********************************************************************\r
4  * Copyright (c) 2006-2009, International Business Machines\r
5  * Corporation and others.  All Rights Reserved.\r
6  **********************************************************************\r
7  * Created on 2006-4-21\r
8  */\r
9 package com.ibm.icu.dev.test;\r
10 \r
11 import java.util.Arrays;\r
12 import java.util.HashMap;\r
13 import java.util.Iterator;\r
14 import java.util.Map;\r
15 import java.util.MissingResourceException;\r
16 import java.util.NoSuchElementException;\r
17 \r
18 import com.ibm.icu.impl.ICUResourceBundle;\r
19 import com.ibm.icu.util.UResourceBundle;\r
20 import com.ibm.icu.util.UResourceBundleIterator;\r
21 import com.ibm.icu.util.UResourceTypeMismatchException;\r
22 \r
23 /**\r
24  * Represents a collection of test data described in a UResourceBoundle file. \r
25  * \r
26  * The root of the UResourceBoundle file is a table resource, and it has one \r
27  * Info and one TestData sub-resources. The Info describes the data module\r
28  * itself. The TestData, which is a table resource, has a collection of test \r
29  * data.\r
30  * \r
31  * The test data is a named table resource which has Info, Settings, Headers,\r
32  * and Cases sub-resources. \r
33  * \r
34  * <pre>\r
35  * DataModule:table(nofallback){ \r
36  *   Info:table {} \r
37  *   TestData:table {\r
38  *     entry_name:table{\r
39  *       Info:table{}\r
40  *       Settings:array{}\r
41  *       Headers:array{}\r
42  *       Cases:array{}\r
43  *     }\r
44  *   } \r
45  * }\r
46  * </pre>\r
47  * \r
48  * The test data is expected to be fed to test code by following sequence \r
49  *\r
50  *   for each setting in Setting{\r
51  *       prepare the setting\r
52  *     for each test data in Cases{\r
53  *       perform the test\r
54  *     }\r
55  *   }\r
56  * \r
57  * For detail of the specification, please refer to the code. The code is \r
58  * initially ported from "icu4c/source/tools/ctestfw/unicode/tstdtmod.h"\r
59  * and should be maintained parallelly.\r
60  * \r
61  * @author Raymond Yang\r
62  */\r
63 class ResourceModule implements TestDataModule {\r
64     private static final String INFO = "Info";\r
65 //    private static final String DESCRIPTION = "Description";\r
66 //    private static final String LONG_DESCRIPTION = "LongDescription";\r
67     private static final String TEST_DATA = "TestData";\r
68     private static final String SETTINGS = "Settings";\r
69     private static final String HEADER = "Headers";\r
70     private static final String DATA = "Cases";\r
71 \r
72     \r
73     UResourceBundle res;\r
74     UResourceBundle info;\r
75     UResourceBundle defaultHeader;\r
76     UResourceBundle testData;\r
77     \r
78     ResourceModule(String baseName, String localeName) throws DataModuleFormatError{\r
79 \r
80         res = (UResourceBundle) UResourceBundle.getBundleInstance(baseName, localeName);\r
81         info = getFromTable(res, INFO, UResourceBundle.TABLE);\r
82         testData = getFromTable(res, TEST_DATA, UResourceBundle.TABLE);\r
83 \r
84         try {\r
85             // unfortunately, actually, data can be either ARRAY or STRING\r
86             defaultHeader = getFromTable(info, HEADER, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});\r
87         } catch (MissingResourceException e){\r
88             defaultHeader = null;\r
89         }\r
90     }\r
91 \r
92     public String getName() {\r
93         return res.getKey();\r
94     }\r
95 \r
96     public DataMap getInfo() {\r
97         return new UTableResource(info);\r
98     }\r
99 \r
100     public TestData getTestData(String testName) throws DataModuleFormatError {\r
101         return new UResourceTestData(defaultHeader, testData.get(testName));\r
102     }\r
103 \r
104     public Iterator getTestDataIterator() {\r
105         return new IteratorAdapter(testData){\r
106             protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {\r
107                 return new UResourceTestData(defaultHeader, nextRes);\r
108             }\r
109         };\r
110     }\r
111 \r
112     /**\r
113      * To make UResourceBundleIterator works like Iterator\r
114      * and return various data-driven test object for next() call\r
115      * \r
116      * @author Raymond Yang\r
117      */\r
118     private abstract static class IteratorAdapter implements Iterator{\r
119         private UResourceBundle res;\r
120         private UResourceBundleIterator itr;\r
121         private Object preparedNextElement = null;\r
122         // fix a strange behavior for UResourceBundleIterator for \r
123         // UResourceBundle.STRING. It support hasNext(), but does \r
124         // not support next() now. \r
125         // \r
126         // Use the iterated resource itself as the result from next() call\r
127         private boolean isStrRes = false;\r
128         private boolean isStrResPrepared = false; // for STRING resouce, we only prepare once\r
129 \r
130         IteratorAdapter(UResourceBundle theRes) {\r
131             assert_not (theRes == null);\r
132             res = theRes;\r
133             itr = ((ICUResourceBundle)res).getIterator();\r
134             isStrRes = res.getType() == UResourceBundle.STRING;\r
135         }\r
136         \r
137         public void remove() {\r
138             // do nothing\r
139         }\r
140 \r
141         private boolean hasNextForStrRes(){\r
142             assert_is (isStrRes);\r
143             assert_not (!isStrResPrepared && preparedNextElement != null);\r
144             if (isStrResPrepared && preparedNextElement != null) return true;\r
145             if (isStrResPrepared && preparedNextElement == null) return false; // only prepare once\r
146             assert_is (!isStrResPrepared && preparedNextElement == null);\r
147             \r
148             try {\r
149                 preparedNextElement = prepareNext(res);\r
150                 assert_not (preparedNextElement == null, "prepareNext() should not return null");\r
151                 isStrResPrepared = true; // toggle the tag\r
152                 return true;\r
153             } catch (DataModuleFormatError e) {\r
154 //#if defined(FOUNDATION10) || defined(J2SE13)\r
155 //##                throw new RuntimeException(e.getMessage());\r
156 //#else\r
157                 throw new RuntimeException(e.getMessage(),e);\r
158 //#endif\r
159             }            \r
160         }\r
161         public boolean hasNext() {\r
162             if (isStrRes) return hasNextForStrRes();\r
163             \r
164             if (preparedNextElement != null) return true;\r
165             UResourceBundle t = null;\r
166             if (itr.hasNext()) {\r
167                 // Notice, other RuntimeException may be throwed\r
168                 t = itr.next();\r
169             } else {\r
170                 return false;\r
171             }\r
172 \r
173             try {\r
174                 preparedNextElement = prepareNext(t);\r
175                 assert_not (preparedNextElement == null, "prepareNext() should not return null");\r
176                 return true;\r
177             } catch (DataModuleFormatError e) {\r
178                 // Sadly, we throw RuntimeException also\r
179 //#if defined(FOUNDATION10) || defined(J2SE13)\r
180 //##                throw new RuntimeException(e.getMessage());\r
181 //#else\r
182                 throw new RuntimeException(e.getMessage(),e);\r
183 //#endif\r
184             }\r
185         }\r
186 \r
187         public Object next(){\r
188             if (hasNext()) {\r
189                 Object t = preparedNextElement;\r
190                 preparedNextElement = null;\r
191                 return t;\r
192             } else {\r
193                 throw new NoSuchElementException();\r
194             }\r
195         }\r
196         /**\r
197          * To prepare data-driven test object for next() call, should not return null\r
198          */\r
199         abstract protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError;\r
200     }\r
201     \r
202     \r
203     /**\r
204      * Avoid use Java 1.4 language new assert keyword \r
205      */\r
206     static void assert_is(boolean eq, String msg){\r
207         if (!eq) throw new Error("test code itself has error: " + msg);\r
208     }\r
209     static void assert_is(boolean eq){\r
210         if (!eq) throw new Error("test code itself has error.");\r
211     }\r
212     static void assert_not(boolean eq, String msg){\r
213         assert_is(!eq, msg);\r
214     }\r
215     static void assert_not(boolean eq){\r
216         assert_is(!eq);\r
217     }\r
218             \r
219     /**\r
220      * Internal helper function to get resource with following add-on \r
221      * \r
222      * 1. Assert the returned resource is never null.\r
223      * 2. Check the type of resource. \r
224      * \r
225      * The UResourceTypeMismatchException for various get() method is a \r
226      * RuntimeException which can be silently bypassed. This behavior is a \r
227      * trouble. One purpose of the class is to enforce format checking for \r
228      * resource file. We don't want to the exceptions are silently bypassed \r
229      * and spreaded to our customer's code. \r
230      * \r
231      * Notice, the MissingResourceException for get() method is also a\r
232      * RuntimeException. The caller functions should avoid sepread the execption\r
233      * silently also. The behavior is modified because some resource are \r
234      * optional and can be missed.\r
235      */\r
236     static UResourceBundle getFromTable(UResourceBundle res, String key, int expResType) throws DataModuleFormatError{\r
237         return getFromTable(res, key, new int[]{expResType});\r
238     }\r
239     \r
240     static UResourceBundle getFromTable(UResourceBundle res, String key, int[] expResTypes) throws DataModuleFormatError{\r
241         assert_is (res != null && key != null && res.getType() == UResourceBundle.TABLE);\r
242         UResourceBundle t = res.get(key); \r
243       \r
244         assert_not (t ==null);\r
245         int type = t.getType();\r
246         Arrays.sort(expResTypes);\r
247         if (Arrays.binarySearch(expResTypes, type) >= 0) {\r
248             return t;\r
249         } else {\r
250 //#if defined(FOUNDATION10) || defined(J2SE13)\r
251 //##            throw new DataModuleFormatError("Actual type " + t.getType() + " != expected types " + expResTypes + ".");\r
252 //#else\r
253             throw new DataModuleFormatError(new UResourceTypeMismatchException("Actual type " + t.getType() + " != expected types " + expResTypes + "."));\r
254 //#endif\r
255         }\r
256     }\r
257     \r
258     /**\r
259      * Unfortunately, UResourceBundle is unable to treat one string as string array.\r
260      * This function return a String[] from UResourceBundle, regardless it is an array or a string \r
261      */\r
262     static String[] getStringArrayHelper(UResourceBundle res, String key) throws DataModuleFormatError{\r
263         UResourceBundle t = getFromTable(res, key, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});\r
264         return getStringArrayHelper(t);\r
265     }\r
266 \r
267     static String[] getStringArrayHelper(UResourceBundle res) throws DataModuleFormatError{\r
268         try{\r
269             int type = res.getType();\r
270             switch (type) {\r
271             case UResourceBundle.ARRAY:\r
272                 return res.getStringArray();\r
273             case UResourceBundle.STRING:\r
274                 return new String[]{res.getString()};\r
275             default:\r
276                 throw new UResourceTypeMismatchException("Only accept ARRAY and STRING types.");\r
277             }\r
278         } catch (UResourceTypeMismatchException e){\r
279 //#if defined(FOUNDATION10) || defined(J2SE13)\r
280 //##            throw new DataModuleFormatError(e.getMessage());\r
281 //#else\r
282             throw new DataModuleFormatError(e);\r
283 //#endif\r
284         }\r
285     }\r
286     \r
287     public static void main(String[] args){\r
288         try {\r
289             TestDataModule m = new ResourceModule("com/ibm/icu/dev/data/testdata/","DataDrivenCollationTest");\r
290         System.out.println("hello: " + m.getName());\r
291         m.getInfo();\r
292         m.getTestDataIterator();\r
293         } catch (DataModuleFormatError e) {\r
294             // TODO Auto-generated catch block\r
295             System.out.println("???");\r
296             e.printStackTrace();\r
297         }\r
298     }\r
299 \r
300     private static class UResourceTestData implements TestData{\r
301         private UResourceBundle res;\r
302         private UResourceBundle info;\r
303         private UResourceBundle settings; \r
304         private UResourceBundle header;\r
305         private UResourceBundle data;\r
306 \r
307         UResourceTestData(UResourceBundle defaultHeader, UResourceBundle theRes) throws DataModuleFormatError{\r
308             \r
309             assert_is (theRes != null && theRes.getType() == UResourceBundle.TABLE);\r
310             res = theRes;\r
311             // unfortunately, actually, data can be either ARRAY or STRING\r
312             data = getFromTable(res, DATA, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});\r
313        \r
314 \r
315             \r
316             try {\r
317                 // unfortunately, actually, data can be either ARRAY or STRING\r
318                 header = getFromTable(res, HEADER, new int[]{UResourceBundle.ARRAY, UResourceBundle.STRING});\r
319             } catch (MissingResourceException e){\r
320                 if (defaultHeader == null) {\r
321                     throw new DataModuleFormatError("Unable to find a header for test data '" + res.getKey() + "' and no default header exist.");\r
322                 } else {\r
323                     header = defaultHeader;\r
324                 }\r
325             }\r
326          try{\r
327                 settings = getFromTable(res, SETTINGS, UResourceBundle.ARRAY);\r
328                 info = getFromTable(res, INFO, UResourceBundle.TABLE);\r
329             } catch (MissingResourceException e){\r
330                 // do nothing, left them null;\r
331                 settings = data;\r
332             }\r
333         }\r
334         \r
335         public String getName() {\r
336             return res.getKey();\r
337         }\r
338 \r
339         public DataMap getInfo() {\r
340             return info == null ? null : new UTableResource(info);\r
341         }\r
342 \r
343         public Iterator getSettingsIterator() {\r
344             assert_is (settings.getType() == UResourceBundle.ARRAY);\r
345             return new IteratorAdapter(settings){\r
346                 protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {\r
347                     return new UTableResource(nextRes);\r
348                 }\r
349             };\r
350         }\r
351 \r
352         public Iterator getDataIterator() {\r
353             // unfortunately,\r
354             assert_is (data.getType() == UResourceBundle.ARRAY \r
355                  || data.getType() == UResourceBundle.STRING);\r
356             return new IteratorAdapter(data){\r
357                 protected Object prepareNext(UResourceBundle nextRes) throws DataModuleFormatError {\r
358                     return new UArrayResource(header, nextRes);\r
359                 }\r
360             };\r
361         }\r
362     }\r
363         \r
364     private static class UTableResource implements DataMap{\r
365         private UResourceBundle res;\r
366 \r
367         UTableResource(UResourceBundle theRes){\r
368             res = theRes;\r
369         }\r
370         public String getString(String key) {\r
371             String t;\r
372             try{\r
373                 t = res.getString(key);\r
374             } catch (MissingResourceException e){\r
375                 t = null;\r
376             }\r
377             return t;\r
378         }\r
379          public Object getObject(String key) {\r
380             \r
381             return res.get(key);\r
382         }\r
383     }\r
384     \r
385     private static class UArrayResource implements DataMap{\r
386         private Map theMap; \r
387         UArrayResource(UResourceBundle theHeader, UResourceBundle theData) throws DataModuleFormatError{\r
388             assert_is (theHeader != null && theData != null);\r
389             String[] header;\r
390          \r
391             header = getStringArrayHelper(theHeader);\r
392             if (theData.getSize() != header.length) \r
393                 throw new DataModuleFormatError("The count of Header and Data is mismatch.");\r
394             theMap = new HashMap();\r
395             for (int i = 0; i < header.length; i++) {\r
396                 if(theData.getType()==UResourceBundle.ARRAY){\r
397                     theMap.put(header[i], theData.get(i));\r
398                 }else if(theData.getType()==UResourceBundle.STRING){\r
399                     theMap.put(header[i], theData.getString());\r
400                 }else{\r
401                     throw new DataModuleFormatError("Did not get the expected data!");                   \r
402                 }\r
403             }\r
404             \r
405         }\r
406         \r
407         public String getString(String key) {\r
408             Object o = theMap.get(key);\r
409             UResourceBundle rb;\r
410             if(o instanceof UResourceBundle) {\r
411                 // unpack ResourceBundle strings\r
412                 rb = (UResourceBundle)o;\r
413                 return rb.getString();\r
414             }\r
415             return (String)o;\r
416         }\r
417         public Object getObject(String key) {\r
418             return theMap.get(key);\r
419         }\r
420     }\r
421 }\r