package beast.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import beast.core.parameter.RealParameter;
import beast.core.util.Log;
@Description("Emulates the behaviour of an Input for constructors annotated with Param annotations.")
public class InputForAnnotatedConstructor<T> extends Input<T> {
/** BEAST object for which to emulate this Input **/
BEASTInterface beastObject;
/** get and set methods **/
Method getter, setter;
public InputForAnnotatedConstructor(BEASTInterface beastObject, Class<?> theClass, Param param) throws NoSuchMethodException, SecurityException, IllegalArgumentException {
if (beastObject == null) {
throw new NullPointerException();
}
this.beastObject = beastObject;
if (theClass == null) {
throw new NullPointerException();
}
this.theClass = theClass;
// TODO: handle defaultValue from Param annotations
// this.defaultValue = param.defaultValue();
if (param.description().trim().length() == 0) {
Log.warning.println("Param annotation found without proper description " + param.toString());
}
this.tipText = param.description();
if (name == null) {
throw new NullPointerException();
}
this.name = param.name();
this.rule = param.optional() ? Validate.OPTIONAL : Validate.REQUIRED;
String methodName = "get" +
name.substring(0, 1).toUpperCase() +
name.substring(1);
try {
getter = beastObject.getClass().getMethod(methodName);
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
Log.err.println("Programmer error: when getting here an InputType was identified, but no getter for Param annotation found");
throw e;
}
methodName = "set" +
name.substring(0, 1).toUpperCase() +
name.substring(1);
try {
setter = beastObject.getClass().getMethod(methodName, theClass);
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
Log.err.println("Programmer error: when getting here an InputType was identified, but no setter for Param annotation found");
throw e;
}
}
@Override
public void setValue(Object value, BEASTInterface beastObject) {
if (value == null) {
if (this.value != null) {
if (this.value instanceof BEASTInterface) {
((BEASTInterface) this.value).getOutputs().remove(beastObject);
}
}
setValue(null);
return;
}
if (value instanceof String) {
try {
setStringValue((String) value);
} catch (Exception e) {
e.printStackTrace();
Log.warning.println("Failed to set the string value to '" + value + "' for beastobject id=" + beastObject.getID());
throw new RuntimeException("Failed to set the string value to '" + value + "' for beastobject id=" + beastObject.getID());
}
} else if (this.value != null && this.value instanceof List<?>) {
if (theClass.isAssignableFrom(value.getClass())) {
// // don't insert duplicates
// RRB: DO insert duplicates: this way CompoundValuable can be set up to
// contain rate matrices with dependent variables/parameters.
// There does not seem to be an example where a duplicate insertion is a problem...
// for (Object o : vector) {
// if (o.equals(value)) {
// return;
// }
// }
setValue(value);
if (value instanceof BEASTInterface) {
((BEASTInterface) value).getOutputs().add(beastObject);
}
} else if (value instanceof List<?> && theClass.isAssignableFrom(((List<?>) value).get(0).getClass())) {
// add all elements in given list to input list.
for (Object v : ((List<?>) value)) {
setValue(v);
if (v instanceof BEASTInterface) {
((BEASTInterface) v).getOutputs().add(beastObject);
}
}
} else {
throw new RuntimeException("Input 101: type mismatch for input " + getName() +
". " + theClass.getName() + ".isAssignableFrom(" + value.getClass() + ")=false");
}
} else {
if (theClass.isAssignableFrom(value.getClass())) {
if (value instanceof BEASTInterface) {
if (this.value != null) {
((BEASTInterface) this.value).getOutputs().remove(beastObject);
}
((BEASTInterface) value).getOutputs().add(beastObject);
}
setValue(value);
} else {
throw new RuntimeException("Input 102: type mismatch for input " + getName());
}
}
}
private void setValue(Object value) {
try {
setter.invoke(beastObject, value);
if (value instanceof BEASTInterface) {
((BEASTInterface) value).getOutputs().add(beastObject);
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Try to parse value of string into Integer, Double or Boolean,
* or it this types differs, just assign as string.
*
* @param valueString value representation
* @throws IllegalArgumentException when all conversions fail
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void setStringValue(final String valueString) {
// figure out the type of T and create object based on T=Integer, T=Double, T=Boolean, T=Valuable
if (value instanceof List<?>) {
List list = (List) get();
list.clear();
// remove start and end spaces
String valueString2 = valueString.replaceAll("^\\s+", "");
valueString2 = valueString2.replaceAll("\\s+$", "");
// split into space-separated bits
String[] valuesString = valueString2.split("\\s+");
for (int i = 0; i < valuesString.length; i++) {
if (theClass.equals(Integer.class)) {
list.add(new Integer(valuesString[i % valuesString.length]));
} else if (theClass.equals(Double.class)) {
list.add(new Double(valuesString[i % valuesString.length]));
} else if (theClass.equals(Boolean.class)) {
String str = valuesString[i % valuesString.length].toLowerCase();
list.add(str.equals("1") || str.equals("true") || str.equals("yes"));
} else if (theClass.equals(String.class)) {
list.add(new String(valuesString[i % valuesString.length]));
}
}
return;
}
if (theClass.equals(Integer.class)) {
setValue(new Integer(valueString));
return;
}
if (theClass.equals(Double.class)) {
setValue(new Double(valueString));
return;
}
if (theClass.equals(Boolean.class)) {
final String valueString2 = valueString.toLowerCase();
if (valueString2.equals("yes") || valueString2.equals("true")) {
setValue(Boolean.TRUE);
return;
} else if (valueString2.equals("no") || valueString2.equals("false")) {
setValue(Boolean.FALSE);
return;
}
}
if (theClass.equals(Function.class)) {
final RealParameter param = new RealParameter();
param.initByName("value", valueString, "upper", 0.0, "lower", 0.0, "dimension", 1);
param.initAndValidate();
setValue(param);
param.getOutputs().add(beastObject);
return;
}
if (theClass.isEnum()) {
if (possibleValues == null) {
possibleValues = (T[]) theClass.getDeclaringClass().getEnumConstants();
}
for (final T t : possibleValues) {
if (valueString.equals(t.toString())) {
setValue(t);
return;
}
}
throw new IllegalArgumentException("Input 104: value " + valueString + " not found. Select one of " + Arrays.toString(possibleValues));
}
// call a string constructor of theClass
try {
Constructor ctor;
Object v = valueString;
try {
ctor = theClass.getDeclaredConstructor(String.class);
} catch (NoSuchMethodException e) {
// we get here if there is not String constructor
// try integer constructor instead
try {
if (valueString.startsWith("0x")) {
v = Integer.parseInt(valueString.substring(2), 16);
} else {
v = Integer.parseInt(valueString);
}
ctor = theClass.getDeclaredConstructor(int.class);
} catch (NumberFormatException e2) {
// could not parse as integer, try double instead
v = Double.parseDouble(valueString);
ctor = theClass.getDeclaredConstructor(double.class);
}
}
ctor.setAccessible(true);
final Object o = ctor.newInstance(v);
setValue(o);
if (o instanceof BEASTInterface) {
((BEASTInterface) o).getOutputs().add(beastObject);
}
} catch (Exception e) {
throw new IllegalArgumentException("Input 103: type mismatch, cannot initialize input '" + getName() +
"' with value '" + valueString + "'.\nExpected something of type " + getType().getName() +
". " + (e.getMessage() != null ? e.getMessage() : ""));
}
} // setStringValue
@SuppressWarnings("unchecked")
@Override
public T get() {
try {
return (T) getter.invoke(beastObject);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}