blob: 858a2e2d3c1da7a3ab372de68255b1ce05bab501 [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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 = "TestData";
/**
* 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;
}
/**
* Return an unmodifiable list of the test data names, in index order.
*/
public List getTestDataNames() {
if (tests != null) {
return Collections.unmodifiableList(Arrays.asList(tests.names()));
}
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;
/**
* changed to fit the c genrb format. this is actually Object[] {Object[]}
*/
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((Object[][])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, (Object[])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 {
return (char[])na.getValue(key);
}
catch (ClassCastException e) {
}
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) {
try {
return (int[])na.getValue(key);
}
catch (ClassCastException e) {
}
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) {
try {
return (byte[])na.getValue(key);
}
catch (ClassCastException e) {
}
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) {
try {
return (boolean[])na.getValue(key);
}
catch (ClassCastException e) {
}
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 + "'");
}
}
}