| package com.ibm.icu.dev.test; |
| |
| import com.ibm.icu.dev.test.TestDataModule.DataMap; |
| import com.ibm.icu.dev.test.TestDataModule.TestData; |
| import com.ibm.icu.impl.ICULocaleData; |
| |
| import java.util.ResourceBundle; |
| |
| /** |
| * Resource-based module. |
| */ |
| public class ResourceModule extends TestDataModule { |
| private NamedArray tests; |
| private RBDataMap info; |
| private String[] defaultHeaders; |
| |
| /** |
| * For internal use. |
| */ |
| static final String TESTS = "Tests"; |
| |
| /** |
| * For internal use. |
| */ |
| static final String INFO = "Info"; |
| |
| /** |
| * For internal use. |
| */ |
| static final String SETTINGS = "Settings"; |
| |
| /** |
| * For internal use. |
| */ |
| static final String CASES = "Cases"; |
| |
| public ResourceModule(String name, TestLog log, ResourceBundle b) { |
| super(name, log); |
| |
| if (b == null) { |
| log.errln("ResourceModule must have non-null bundle"); |
| return; |
| } |
| |
| Object[][] data = (Object[][])b.getObject(TESTS); |
| if (data == null) { |
| log.errln("ResourceModule does not contain tests!"); |
| return; |
| } |
| this.tests = NamedArray.create(data); |
| |
| Object[][] temp = (Object[][])b.getObject(INFO); |
| if (temp != null) { |
| NamedArray na = NamedArray.create(temp); |
| if (na.count() < 0) { |
| log.errln("Bad data for " + INFO); |
| } else { |
| this.info = new RBDataMap(log, na, false); |
| this.defaultHeaders = info.getStringArray(HEADERS); |
| } |
| } |
| } |
| |
| /** |
| * Get additional data related to the module. |
| * Returns true if successfully got info. |
| */ |
| public DataMap getInfo() { |
| return info; |
| } |
| |
| /** |
| * Returns the Test object corresponding to name, |
| * or null if name not found in this module. |
| */ |
| public TestData createTestData(String name) { |
| if (tests != null) { |
| Object[] td = tests.getEntry(name); |
| if (td != null) { |
| return createTestData(td); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the Test object corresponding to index, |
| * or null if index is out of range for this module. |
| * No error logged if index is out of bounds, the assumption is that |
| * iteration is being used and proceeds until a null return. |
| */ |
| public TestData createTestData(int index) { |
| if (tests != null) { |
| Object[] td = tests.getEntry(index); |
| if (td != null) { |
| return createTestData(td); |
| } |
| } |
| return null; |
| } |
| |
| private TestData createTestData(Object[] d) { |
| return new RBTestData(this, (String)d[0], (Object[][])d[1], defaultHeaders); |
| } |
| |
| /** |
| * Provides access to values either by index or by case-insensitive search on |
| * the name. Poor man's Map with index. |
| */ |
| static abstract class NamedArray { |
| int txcache; |
| int count; |
| |
| static NamedArray create(Object[][] pairs) { |
| return new NamedArrayOfPairs(pairs); |
| } |
| |
| static NamedArray create(String[] names, Object[] values) { |
| return new NamedArrayTwoLists(names, values); |
| } |
| |
| protected NamedArray(int count) { |
| this.count = count; |
| } |
| |
| public final int count() { |
| return count; |
| } |
| |
| public final boolean isDefined(String name) { |
| int ix = nameToIndex(name); |
| if (ix != -1) { |
| txcache = (ix == 0 ? count : ix) - 1; |
| return true; |
| } |
| return false; |
| } |
| |
| public final Object[] getEntry(String name) { |
| return getEntry(nameToIndex(name)); |
| } |
| |
| public final Object getValue(String name) { |
| return getValue(nameToIndex(name)); |
| } |
| |
| public final Object[] getEntry(int index) { |
| if (index >= 0 && index < count) { |
| return entryAtIndex(index); |
| } |
| return null; |
| } |
| |
| public final Object getValue(int index) { |
| if (index >= 0 && index < count) { |
| return valueAtIndex(index); |
| } |
| return null; |
| } |
| |
| public String[] names() { |
| if (count > 0) { |
| String[] result = new String[count]; |
| for (int i = 0; i < count; ++i) { |
| result[i] = nameAtIndex(i); |
| } |
| return result; |
| } |
| return null; |
| } |
| |
| public Object[] values() { |
| if (count > 0) { |
| Object[] result = new Object[count]; |
| for (int i = 0; i < count; ++i) { |
| result[i] = valueAtIndex(i); |
| } |
| return result; |
| } |
| return null; |
| } |
| |
| public String toString() { |
| StringBuffer buf = new StringBuffer(super.toString()); |
| buf.append("count: " + count + "{"); |
| for (int i = 0; i < count; ++i) { |
| buf.append("\n { " + nameAtIndex(i) + ", " + valueAtIndex(i) + " }"); |
| } |
| buf.append("}"); |
| return buf.toString(); |
| } |
| |
| protected int nameToIndex(String name) { |
| if (name != null && count > 0) { |
| int i = txcache; |
| do { |
| if (++i == count) { |
| i = 0; |
| } |
| if (name.equalsIgnoreCase(nameAtIndex(i))) { |
| txcache = i; |
| return i; |
| } |
| } while (i != txcache); |
| } |
| return -1; |
| } |
| |
| protected Object[] entryAtIndex(int index) { |
| return new Object[] { nameAtIndex(index), valueAtIndex(index) }; |
| } |
| |
| protected abstract String nameAtIndex(int index); |
| protected abstract Object valueAtIndex(int index); |
| } |
| |
| /** |
| * NamedArray implemented using an array of name/value |
| * pairs, represented by an Object[][]. |
| */ |
| static final class NamedArrayOfPairs extends NamedArray { |
| private Object[][] data; |
| |
| public NamedArrayOfPairs(Object[][] data) { |
| super(data == null ? -1 : data.length); |
| this.data = data; |
| } |
| |
| protected Object[] entryAtIndex(int index) { |
| return data[index]; |
| } |
| |
| protected String nameAtIndex(int index) { |
| return (String)data[index][0]; |
| } |
| |
| protected Object valueAtIndex(int index) { |
| return data[index][1]; |
| } |
| } |
| |
| /** |
| * NamedArray implemented using two arrays, one of String and one of Object. |
| */ |
| static final class NamedArrayTwoLists extends NamedArray { |
| String[] names; |
| Object[] values; |
| |
| public NamedArrayTwoLists(String[] names, Object[] values) { |
| super (values != null && names != null && names.length == values.length ? |
| names.length : |
| -1); |
| this.names = names; |
| this.values = values; |
| } |
| |
| public String[] names() { |
| return names; |
| } |
| |
| public Object[] values() { |
| return values; |
| } |
| |
| protected String nameAtIndex(int index) { |
| return names[index]; |
| } |
| |
| protected Object valueAtIndex(int index) { |
| return values[index]; |
| } |
| } |
| |
| static class RBTestData extends TestData { |
| ResourceModule m; |
| RBDataMap info; |
| Object[][][] settings; |
| Object[][] cases; |
| String[] headers; |
| int sx; |
| int cx; |
| |
| RBTestData(ResourceModule m, String name, Object[][] data, String[] defaultHeaders) { |
| super(name); |
| this.m = m; |
| |
| NamedArray namedData = NamedArray.create(data); |
| |
| try { |
| Object[][] temp = (Object[][])namedData.getValue(INFO); |
| if (temp != null) { |
| NamedArray na = NamedArray.create(temp); |
| if (na.count() < 1) { |
| m.log.errln("Bad data for " + INFO); |
| } else { |
| this.info = new RBDataMap(m.log, na, false); |
| } |
| } |
| } |
| catch (ClassCastException e) { |
| m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + INFO); |
| } |
| |
| try { |
| this.settings = (Object[][][])namedData.getValue(SETTINGS); |
| } |
| catch (ClassCastException e) { |
| m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + SETTINGS); |
| } |
| |
| try { |
| this.cases = (Object[][])namedData.getValue(CASES); |
| } |
| catch (ClassCastException e) { |
| m.log.errln("Test " + name + " in module " + m.getName() + " has bad type for " + CASES); |
| } |
| |
| if (info != null) { |
| this.headers = info.getStringArray(HEADERS); |
| } |
| if (this.headers == null) { |
| this.headers = defaultHeaders; |
| } |
| } |
| |
| /** |
| * Get additional data related to the test. |
| */ |
| public DataMap getInfo() { |
| return info; |
| } |
| |
| /** |
| * Returns DataMap with next settings and resets case iteration. |
| * If no more settings, terminates iteration and returns null. |
| */ |
| public DataMap nextSettings() { |
| if (settings != null && sx < settings.length) { |
| cx = 0; |
| NamedArray na = NamedArray.create(settings[sx++]); |
| if (na.count() < 0) { |
| m.log.errln("Bad settings data for settings " + (sx-1)); |
| return null; |
| } else { |
| return new RBDataMap(m.log, na, false); |
| } |
| } |
| stopIteration(); |
| |
| return null; |
| } |
| |
| /** |
| * Returns DataMap with next case. If no next case, returns null. |
| */ |
| public DataMap nextCase() { |
| if (cases != null && cx < cases.length) { |
| NamedArray na = NamedArray.create(headers, cases[cx++]); |
| if (na.count() < 0) { |
| m.log.errln("Bad cases data for case " + (cx-1)); |
| } else { |
| return new RBDataMap(m.log, na, true); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Stops iteration. |
| */ |
| public void stopIteration() { |
| sx = cx = Integer.MAX_VALUE; |
| } |
| } |
| |
| static final class RBDataMap extends DataMap { |
| TestLog log; |
| NamedArray na; |
| boolean required; |
| |
| RBDataMap(TestLog log, NamedArray na, boolean required) { |
| this.log = log; |
| this.na = na; |
| this.required = required; |
| } |
| |
| public boolean isDefined(String key) { |
| return na.isDefined(key); |
| } |
| |
| public Object getObject(String key) { |
| Object result = na.getValue(key); |
| if (required && result == null) { |
| reportNullError(key); |
| } |
| return result; |
| } |
| |
| public String getString(String key) { |
| String result = null; |
| try { |
| result = (String)getObject(key); |
| } |
| catch (ClassCastException e) { |
| reportTypeError("String", key); |
| } |
| return result; |
| } |
| |
| public char getChar(String key) { |
| try { |
| String s = getString(key); |
| if (s != null) { |
| return s.charAt(0); |
| } |
| } |
| catch (IndexOutOfBoundsException e) { |
| reportTypeError("char", key); |
| } |
| return 0xffff; |
| } |
| |
| public int getInt(String key) { |
| try { |
| String s = getString(key); |
| if (s != null) { |
| return Integer.parseInt(s); |
| } |
| } |
| catch (NumberFormatException e) { |
| reportTypeError("int", key); |
| } |
| return -1; |
| } |
| |
| public byte getByte(String key) { |
| try { |
| String s = getString(key); |
| if (s != null) { |
| return Byte.parseByte(s); |
| } |
| } |
| catch (NumberFormatException e) { |
| reportTypeError("byte", key); |
| } |
| return (byte)-1; |
| } |
| |
| public boolean getBoolean(String key) { |
| String s = getString(key); |
| if (s != null) { |
| if (s.equalsIgnoreCase("true")) { |
| return true; |
| } else if (!s.equalsIgnoreCase("false")) { |
| reportTypeError("boolean", key); |
| } |
| } |
| return false; |
| } |
| |
| public Object[] getObjectArray(String key) { |
| try { |
| Object[] result = (Object[])na.getValue(key); |
| if (result == null && required) { |
| reportNullError(key); |
| } |
| } catch (ClassCastException e) { |
| reportTypeError("Object[]", key); |
| } |
| return null; |
| } |
| |
| public String[] getStringArray(String key) { |
| try { |
| String[] result = (String[])na.getValue(key); |
| if (result == null && required) { |
| reportNullError(key); |
| } |
| return result; |
| } |
| catch (ClassCastException e) { |
| reportTypeError("String[]", key); |
| } |
| return null; |
| } |
| |
| public char[] getCharArray(String key) { |
| try { |
| String temp = (String)na.getValue(key); |
| if (temp == null) { |
| if (required) { |
| reportNullError(key); |
| } |
| } else { |
| return temp.toCharArray(); |
| } |
| } |
| catch (ClassCastException e) { |
| reportTypeError("char[]", key); |
| } |
| return null; |
| } |
| |
| public int[] getIntArray(String key) { |
| String[] data = getStringArray(key); |
| if (data != null) { |
| try { |
| int[] result = new int[data.length]; |
| for (int i = 0; i < data.length; ++i) { |
| result[i] = Integer.parseInt(data[i]); |
| } |
| return result; |
| } |
| catch (NumberFormatException e) { |
| reportTypeError("int[]", key); |
| } |
| } |
| return null; |
| } |
| |
| public byte[] getByteArray(String key) { |
| String[] data = getStringArray(key); |
| if (data != null) { |
| try { |
| byte[] result = new byte[data.length]; |
| for (int i = 0; i < result.length; ++i) { |
| result[i] = Byte.parseByte(data[i]); |
| } |
| return result; |
| } |
| catch (NumberFormatException e) { |
| reportTypeError("byte[]", key); |
| } |
| } |
| return null; |
| } |
| |
| public boolean[] getBooleanArray(String key) { |
| String[] data = getStringArray(key); |
| if (data != null) { |
| boolean[] result = new boolean[data.length]; |
| for (int i = 0; i < result.length; ++i) { |
| String s = data[i]; |
| if (s.equalsIgnoreCase("true")) { |
| result[i] = true; |
| } else if (s.equalsIgnoreCase("false")) { |
| result[i] = false; |
| } else { |
| reportTypeError("boolean[]", key); |
| return null; |
| } |
| } |
| return result; |
| } |
| return null; |
| } |
| |
| private void reportNullError(String key) { |
| log.errln("Missing required value for '" + key + "'"); |
| } |
| |
| private void reportTypeError(String typeName, String key) { |
| log.errln("Could not return value of '" + key + "' (" + getObject(key) + ") as type '" + typeName + "'"); |
| } |
| } |
| } |