// Modified or written by Object Mentor, Inc. for inclusion with FitNesse.
// Copyright (c) 2002 Cunningham & Cunningham, Inc.
// Released under the terms of the GNU General Public License version 2 or later.
package fit;
// Copyright (c) 2002 Cunningham & Cunningham, Inc.
// Released under the terms of the GNU General Public License version 2 or later.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
public class TypeAdapter {
public Object target;
public Fixture fixture;
public Field field;
public Method method;
public Class<?> type;
public boolean isRegex;
private static final Map<Class<?>, TypeAdapter> PARSE_DELEGATES = new HashMap<>();
// Factory //////////////////////////////////
public static TypeAdapter on(Fixture target, Class<?> type) {
TypeAdapter a = adapterFor(type);
a.init(target, type);
return a;
}
public static TypeAdapter on(Fixture fixture, Field field) {
TypeAdapter a = on(fixture, field.getType());
a.target = fixture;
a.field = field;
a.field.setAccessible(true);
return a;
}
public static TypeAdapter on(Fixture fixture, Method method) {
return on(fixture, method, false);
}
public static TypeAdapter on(Fixture fixture, Method method, boolean isRegex) {
TypeAdapter a = on(fixture, method.getReturnType());
a.target = fixture;
a.method = method;
a.isRegex = isRegex;
return a;
}
public static TypeAdapter adapterFor(Class<?> type) throws UnsupportedOperationException {
if (type.isPrimitive()) {
if (type.equals(byte.class)) return new ByteAdapter();
if (type.equals(short.class)) return new ShortAdapter();
if (type.equals(int.class)) return new IntAdapter();
if (type.equals(long.class)) return new LongAdapter();
if (type.equals(float.class)) return new FloatAdapter();
if (type.equals(double.class)) return new DoubleAdapter();
if (type.equals(char.class)) return new CharAdapter();
if (type.equals(boolean.class)) return new BooleanAdapter();
throw new UnsupportedOperationException("can't yet adapt " + type);
} else {
Object delegate = PARSE_DELEGATES.get(type);
if (delegate instanceof DelegateClassAdapter)
return (TypeAdapter) ((DelegateClassAdapter) delegate).clone();
if (delegate instanceof DelegateObjectAdapter)
return (TypeAdapter) ((DelegateObjectAdapter) delegate).clone();
if (type.equals(Byte.class)) return new ClassByteAdapter();
if (type.equals(Short.class)) return new ClassShortAdapter();
if (type.equals(Integer.class)) return new ClassIntegerAdapter();
if (type.equals(Long.class)) return new ClassLongAdapter();
if (type.equals(Float.class)) return new ClassFloatAdapter();
if (type.equals(Double.class)) return new ClassDoubleAdapter();
if (type.equals(Character.class)) return new ClassCharacterAdapter();
if (type.equals(Boolean.class)) return new ClassBooleanAdapter();
if (type.isArray()) return new ArrayAdapter();
return new TypeAdapter();
}
}
// Accessors ////////////////////////////////
public void init(Fixture fixture, Class<?> type) {
this.fixture = fixture;
this.type = type;
}
public Object get() throws IllegalAccessException, InvocationTargetException {
if (field != null) {
return field.get(target);
}
if (method != null) {
return invoke();
}
return null;
}
public void set(Object value) throws Exception {
field.set(target, value);
}
public Object invoke() throws IllegalAccessException, InvocationTargetException {
Object[] params = {};
return method.invoke(target, params);
}
public Object parse(String s) throws Exception {
Object obj;
obj = isRegex ? s : fixture.parse(s, type);
return obj;
}
public boolean equals(Object a, Object b) {
boolean isEqual = false;
if (isRegex) {
if (b != null)
isEqual = Pattern.matches(a.toString(), b.toString());
} else {
if (a == null)
isEqual = (b == null);
else
isEqual = a.equals(b);
}
return isEqual;
}
public String toString(Object o) {
if (o == null) {
return "null";
} else if (o instanceof String && ((String) o).equals(""))
return "blank";
else
return o.toString();
}
/*
* Registers a delegate, a class that will handle parsing of other types of values.
*/
public static void registerParseDelegate(Class<?> type, Class<?> parseDelegate) {
try {
PARSE_DELEGATES.put(type, new DelegateClassAdapter(parseDelegate));
} catch (Exception ex) {
throw new RuntimeException("Parse delegate class " + parseDelegate.getName()
+ " does not have a suitable static parse() method.");
}
}
/*
* Registers a delegate object that will handle parsing of other types of values.
*/
public static void registerParseDelegate(Class<?> type, Object parseDelegate) {
try {
PARSE_DELEGATES.put(type, new DelegateObjectAdapter(parseDelegate));
} catch (Exception ex) {
throw new RuntimeException("Parse delegate object of class " + parseDelegate.getClass().getName()
+ " does not have a suitable parse() method.");
}
}
public static void clearDelegatesForNextTest() {
PARSE_DELEGATES.clear();
}
// Subclasses ///////////////////////////////
static class ByteAdapter extends ClassByteAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setByte(target, ((Byte) i).byteValue());
}
}
static class ClassByteAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Byte(Byte.parseByte(s));
}
}
static class ShortAdapter extends ClassShortAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setShort(target, ((Short) i).shortValue());
}
}
static class ClassShortAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Short(Short.parseShort(s));
}
}
static class IntAdapter extends ClassIntegerAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setInt(target, ((Integer) i).intValue());
}
}
static class ClassIntegerAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Integer(Integer.parseInt(s));
}
}
static class LongAdapter extends ClassLongAdapter {
public void set(Long i) throws IllegalAccessException {
field.setLong(target, i.longValue());
}
}
static class ClassLongAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Long(Long.parseLong(s));
}
}
static class FloatAdapter extends ClassFloatAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setFloat(target, ((Number) i).floatValue());
}
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Float(Float.parseFloat(s));
}
}
static class ClassFloatAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Float(Float.parseFloat(s));
}
}
static class DoubleAdapter extends ClassDoubleAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setDouble(target, ((Number) i).doubleValue());
}
@Override
public Object parse(String s) {
return new Double(Double.parseDouble(s));
}
}
static class ClassDoubleAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Double(Double.parseDouble(s));
}
}
static class CharAdapter extends ClassCharacterAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setChar(target, ((Character) i).charValue());
}
}
static class ClassCharacterAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
return ("null".equals(s)) ? null : new Character(s.charAt(0));
}
}
static class BooleanAdapter extends ClassBooleanAdapter {
@Override
public void set(Object i) throws IllegalAccessException {
field.setBoolean(target, ((Boolean) i).booleanValue());
}
}
static class ClassBooleanAdapter extends TypeAdapter {
@Override
public Object parse(String s) {
if ("null".equals(s)) return null;
String ls = s.toLowerCase();
if (ls.equals("true"))
return Boolean.TRUE;
if (ls.equals("yes"))
return Boolean.TRUE;
if (ls.equals("1"))
return Boolean.TRUE;
if (ls.equals("y"))
return Boolean.TRUE;
if (ls.equals("+"))
return Boolean.TRUE;
return Boolean.FALSE;
}
}
static class ArrayAdapter extends TypeAdapter {
Class<?> componentType;
TypeAdapter componentAdapter;
@Override
public void init(Fixture target, Class<?> type) {
super.init(target, type);
componentType = type.getComponentType();
componentAdapter = on(target, componentType);
}
@Override
public Object parse(String s) throws Exception {
StringTokenizer t = new StringTokenizer(s, ",");
Object array = Array.newInstance(componentType, t.countTokens());
for (int i = 0; t.hasMoreTokens(); i++) {
Array.set(array, i, componentAdapter.parse(t.nextToken().trim()));
}
return array;
}
@Override
public String toString(Object o) {
if (o == null)
return "";
int length = Array.getLength(o);
StringBuilder b = new StringBuilder(5 * length);
for (int i = 0; i < length; i++) {
b.append(componentAdapter.toString(Array.get(o, i)));
if (i < (length - 1)) {
b.append(", ");
}
}
return b.toString();
}
@Override
public boolean equals(Object a, Object b) {
int length = Array.getLength(a);
if (length != Array.getLength(b))
return false;
for (int i = 0; i < length; i++) {
if (!componentAdapter.equals(Array.get(a, i), Array.get(b, i)))
return false;
}
return true;
}
}
static class DelegateClassAdapter extends TypeAdapter implements Cloneable {
private Method parseMethod;
public DelegateClassAdapter(Class<?> parseDelegate) throws SecurityException, NoSuchMethodException {
this.parseMethod = parseDelegate.getMethod("parse", new Class[]{String.class});
int modifiers = parseMethod.getModifiers();
if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)
|| parseMethod.getReturnType() == Void.class)
throw new NoSuchMethodException();
}
@Override
public Object parse(String s) throws Exception {
return parseMethod.invoke(null, new Object[]
{s});
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
static class DelegateObjectAdapter extends TypeAdapter implements Cloneable {
private Object delegate;
private Method parseMethod;
public DelegateObjectAdapter(Object delegate) throws SecurityException, NoSuchMethodException {
this.delegate = delegate;
this.parseMethod = delegate.getClass().getMethod("parse", new Class[]
{String.class});
}
@Override
public Object parse(String s) throws Exception {
return parseMethod.invoke(delegate, new Object[]
{s});
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
}