// 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;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import fit.exception.FitFailureException;
import fit.exception.NoSuchFieldFitFailureException;
import fit.exception.NoSuchMethodFitFailureException;
import util.GracefulNamer;
import static java.util.Arrays.asList;
public abstract class Binding {
private static final Pattern regexMethodPattern = Pattern.compile("(.+)(?:\\?\\?|!!)");
private static final Pattern methodPattern = Pattern.compile("(.+)(?:\\(\\)|\\?|!)");
private static final Pattern fieldPattern = Pattern.compile("=?([^=]+)=?");
public TypeAdapter adapter;
public static Binding create(Fixture fixture, String name) throws Throwable {
Binding binding = null;
if (name.startsWith("="))
binding = new SaveBinding();
else if (name.endsWith("="))
binding = new RecallBinding();
else if (regexMethodPattern.matcher(name).matches())
binding = new RegexQueryBinding();
else if (methodPattern.matcher(name).matches())
binding = new QueryBinding();
else if (fieldPattern.matcher(name).matches())
binding = new SetBinding();
if (binding == null)
binding = new NullBinding();
else
binding.adapter = makeAdapter(fixture, name);
return binding;
}
private static TypeAdapter makeAdapter(Fixture fixture, String name) throws Throwable {
Matcher regexMatcher = regexMethodPattern.matcher(name);
if (regexMatcher.find())
return makeAdapterForRegexMethod(name, fixture, regexMatcher);
else {
Matcher methodMatcher = methodPattern.matcher(name);
if (methodMatcher.find())
return makeAdapterForMethod(name, fixture, methodMatcher);
else
return makeAdapterForField(name, fixture);
}
}
private static TypeAdapter makeAdapterForField(String name, Fixture fixture) {
Field field = null;
if (GracefulNamer.isGracefulName(name)) {
String simpleName = GracefulNamer.disgrace(name).toLowerCase();
field = findField(fixture, simpleName);
} else {
Matcher matcher = fieldPattern.matcher(name);
matcher.find();
String fieldName = matcher.group(1);
Class<?> clazz = fixture.getTargetClass();
try {
field = clazz.getField(fieldName);
}
catch (NoSuchFieldException e) {
try {
field = getField(clazz, fieldName);
} catch (NoSuchFieldException e2) {
}
}
}
if (field == null)
throw new NoSuchFieldFitFailureException(name);
return TypeAdapter.on(fixture, field);
}
private static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class<?> superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
private static TypeAdapter makeAdapterForMethod(String name, Fixture fixture, Matcher matcher) {
Method method = getMethod(name, fixture, matcher);
if (method == null)
throw new NoSuchMethodFitFailureException(name);
return TypeAdapter.on(fixture, method, false);
}
private static TypeAdapter makeAdapterForRegexMethod(String name, Fixture fixture, Matcher matcher) {
Method method = getMethod(name, fixture, matcher);
if (method == null)
throw new NoSuchMethodFitFailureException(name);
return TypeAdapter.on(fixture, method, true);
}
private static Method getMethod(String name, Fixture fixture, Matcher matcher) {
Method method = null;
if (GracefulNamer.isGracefulName(name)) {
String simpleName = GracefulNamer.disgrace(name).toLowerCase();
method = findMethod(fixture, simpleName);
} else {
try {
String methodName = matcher.group(1);
method = fixture.getTargetClass().getMethod(methodName, new Class[]{});
}
catch (NoSuchMethodException e) {
}
}
return method;
}
private static Field findField(Fixture fixture, String simpleName) {
List<Field> fields = getAllDeclaredFields(fixture.getTargetClass());
Field field = null;
for (int i = 0; i < fields.size(); i++) {
Field possibleField = fields.get(i);
if (simpleName.equals(possibleField.getName().toLowerCase())) {
field = possibleField;
break;
}
}
return field;
}
private static List<Field> getAllDeclaredFields(Class<?> clazz){
if (clazz.getSuperclass() != null) {
List<Field> fields = getAllDeclaredFields(clazz.getSuperclass());
fields.addAll(asList(clazz.getDeclaredFields()));
return fields;
} else {
return new ArrayList(asList(clazz.getDeclaredFields()));
}
}
private static Method findMethod(Fixture fixture, String simpleName) {
Method[] methods = fixture.getTargetClass().getMethods();
Method method = null;
for (int i = 0; i < methods.length; i++) {
Method possibleMethod = methods[i];
if (simpleName.equals(possibleMethod.getName().toLowerCase())) {
method = possibleMethod;
break;
}
}
return method;
}
public abstract void doCell(Fixture fixture, Parse cell) throws Throwable;
public static class SaveBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) {
try {
//TODO-MdM hmm... somehow this needs to regulated by the fixture.
if (fixture instanceof ColumnFixture)
((ColumnFixture) fixture).executeIfNeeded();
Object valueObj = adapter.get(); //...might be validly null
String symbolValue = valueObj == null ? "null" : valueObj.toString();
String symbolName = cell.text();
Fixture.setSymbol(symbolName, symbolValue);
cell.addToBody(Fixture.gray(" = " + symbolValue));
}
catch (Exception e) {
fixture.exception(cell, e);
}
}
}
public static class RecallBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) throws Exception {
String symbolName = cell.text();
if (!Fixture.hasSymbol(symbolName))
fixture.exception(cell, new FitFailureException("No such symbol: " + symbolName));
else {
String value = (String) Fixture.getSymbol(symbolName);
if (adapter.field != null) {
adapter.set(adapter.parse(value));
cell.addToBody(Fixture.gray(" = " + value));
}
if (adapter.method != null) {
cell.body = value;
fixture.check(cell, adapter);
}
}
}
}
public static class SetBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) throws Throwable {
if ("".equals(cell.text()))
fixture.handleBlankCell(cell, adapter);
adapter.set(adapter.parse(cell.text()));
}
}
public static class QueryBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) {
fixture.check(cell, adapter);
}
}
public static class RegexQueryBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) {
fixture.check(cell, adapter);
}
}
public static class NullBinding extends Binding {
public void doCell(Fixture fixture, Parse cell) {
fixture.ignore(cell);
}
}
}