package org.trianacode.taskgraph;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Takes a class in the constructor and creates inputs to a selected method.
* Outputs the result of invoking the method, or if a primitive or collection or enum,
* outputs the input.
*
* @author Andrew Harrison
* @version $Revision: 1.16 $
* @created 03 Jun 2006
* @date $Date: 2004/06/11 15:59:20 $ modified by $Author: spxinw $
*/
public class BeanUnit extends Unit {
private List<Method> setters = new ArrayList<Method>();
private List<Method> getters = new ArrayList<Method>();
private Method selectedMethod = null;
private String[] currentInputs = new String[0];
private String[] currentOutputs = new String[0];
private Method[] ignores = Object.class.getMethods();
private Object bean;
private Class beanClass;
private String primitiveValue = null;
private boolean isList = false;
private boolean showMethodChoice = false;
private boolean returnMe = false;
public BeanUnit(Class beanClass) throws Exception {
this(beanClass, null);
}
public BeanUnit(Class beanClass, Method defaultMethod) throws Exception {
log("class name:" + beanClass.getName());
this.beanClass = wrapPrimitiveClass(beanClass);
if (!isArray(beanClass) && (beanClass.isInterface() || Modifier.isAbstract(beanClass.getModifiers()))) {
throw new IllegalArgumentException("Class cannot be be an interface or abstract");
}
if (isArrayOrCollection(this.beanClass)) {
if (isCollection(this.beanClass)) {
bean = this.beanClass.newInstance();
} else {
bean = Array.newInstance(Object.class, 0);
}
if (isArray(beanClass)) {
currentInputs = new String[]{beanClass.getComponentType().getName()};
} else {
currentInputs = new String[]{Object.class.getName()};
}
currentOutputs = new String[]{beanClass.getName()};
isList = true;
} else if (!isPrimitiveOrWrapperOrEnum(this.beanClass)) {
this.bean = this.beanClass.newInstance();
List<Method>[] arr = extractMethods(this.beanClass);
this.setters = arr[0];
this.getters = arr[1];
if (defaultMethod == null) {
if (setters.size() > 0 && getters.size() > 0) {
showMethodChoice = true;
this.selectedMethod = setters.get(0);
setIOTypes(selectedMethod);
} else {
showMethodChoice = false;
if (setters.size() == 0) {
currentInputs = new String[]{this.beanClass.getName()};
if (getters.size() > 0) {
this.selectedMethod = getters.get(0);
currentOutputs = new String[]{this.selectedMethod.getReturnType().getName()};
}
}
if (getters.size() == 0) {
currentOutputs = new String[]{this.beanClass.getName()};
returnMe = true;
}
}
} else {
showMethodChoice = true;
this.selectedMethod = defaultMethod;
setIOTypes(selectedMethod);
}
} else {
currentInputs = new String[]{this.beanClass.getName()};
currentOutputs = new String[]{this.beanClass.getName()};
}
}
/*
* Called whenever there is data for the unit to process
*/
public void process() {
Method m = selectedMethod;
if (m != null) {
if (isGetter(m)) {
try {
Object ret = m.invoke(bean, new Object[0]);
if (ret != null) {
output(ret);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
List<Object> params = new ArrayList<Object>();
for (int count = 0; count < getInputNodeCount(); count++) {
Object obj = getInputAtNode(count);
params.add(obj);
}
try {
m.invoke(bean, params.toArray(new Object[params.size()]));
Method getter = getMatchingMethod(m);
Object ret = getter.invoke(bean, null);
if (ret != null) {
output(ret);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
if (primitiveValue != null) {
setValue(primitiveValue);
} else if (returnMe) {
this.bean = this;
} else {
if (isList) {
List<Object> params = new ArrayList<Object>();
for (int count = 0; count < getInputNodeCount(); count++) {
Object obj = getInputAtNode(count);
params.add(obj);
}
if (isArray(beanClass)) {
bean = Array.newInstance(Object.class, params.size());
for (int i = 0; i < params.size(); i++) {
Object o = params.get(i);
Array.set(bean, i, o);
}
} else {
for (Object param : params) {
((Collection) bean).add(param);
}
}
} else if (getInputAtNode(0) != null) {
Object obj = getInputAtNode(0);
this.bean = obj;
}
}
output(bean);
}
}
private boolean isGetter(Method m) {
if (m.getName().startsWith("get") || m.getName().startsWith("is")) {
if (m.getParameterTypes().length == 0) {
return true;
}
}
return false;
}
private List<Method>[] extractMethods(Class o) {
Method[] ms = o.getDeclaredMethods();
List<Method> s = new ArrayList<Method>();
List<Method> g = new ArrayList<Method>();
for (Method m : ms) {
if (!Modifier.isPublic(m.getModifiers())) {
continue;
}
boolean not = false;
for (Method ignore : ignores) {
if (m.equals(ignore)) {
not = true;
break;
}
}
if (not) {
continue;
}
if (m.getName().length() > 4 && m.getName().startsWith("set")) {
String field = m.getName().substring(3, m.getName().length());
field = field.substring(0, 1).toLowerCase() + field.substring(1, field.length());
Field[] fs = o.getDeclaredFields();
for (Field f : fs) {
if (f.getName().equals(field)) ;
s.add(m);
break;
}
} else if ((m.getName().length() > 4 && m.getName().startsWith("get")) || (m.getName().length() > 4
&& m.getName().startsWith("is"))) {
String name = m.getName();
int offset;
offset = name.startsWith("get") ? 3 : 2;
String field = name.substring(offset, name.length());
field = field.substring(0, 1).toLowerCase() + field.substring(1, field.length());
Field[] fs = o.getDeclaredFields();
for (Field f : fs) {
if (f.getName().equals(field)) ;
g.add(m);
break;
}
}
}
return new List[]{s, g};
}
/**
* Called when the unit is created. Initialises the unit's properties and
* parameters.
*/
public void init() {
super.init();
// Initialise node properties
setDefaultInputNodes(0);
setMinimumInputNodes(0);
setMaximumInputNodes(Integer.MAX_VALUE);
setDefaultOutputNodes(0);
setMinimumOutputNodes(0);
setMaximumOutputNodes(Integer.MAX_VALUE);
// Initialise parameter update policy and output policy
setParameterUpdatePolicy(PROCESS_UPDATE);
setOutputPolicy(COPY_OUTPUT);
// Initialise pop-up description and help file location
setPopUpDescription("Takes a bean object and invokes the chosen method on it.");
setHelpFileLocation("BeanUnit.html");
if (selectedMethod != null) {
if (showMethodChoice && setters.size() > 1) {
defineParameter("selectedMethod", selectedMethod.getName(), USER_ACCESSIBLE);
StringBuilder sb = new StringBuilder();
sb.append("Method").append(" $title ").append("selectedMethod").append(" Choice");
for (Method value : setters) {
sb.append(" ").append(value.getName());
}
setGUIBuilderV2Info(sb.toString() + "\n");
}
try {
getTask().setSubTitle(selectedMethod.getName());
updateInputNodes();
updateOutputNodes();
} catch (NodeException e) {
e.printStackTrace();
}
} else {
if (!isList) {
if (isEnum(beanClass)) {
Object[] consts = beanClass.getEnumConstants();
if (consts.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("Constant").append(" $title ").append("value").append(" Choice");
for (Object con : consts) {
sb.append(" ").append(con.toString());
}
defineParameter("value", consts[0].toString(), USER_ACCESSIBLE);
setGUIBuilderV2Info(sb.toString() + "\n");
}
} else {
defineParameter("value", "", USER_ACCESSIBLE);
setGUIBuilderV2Info("Value $title value TextField\n");
}
}
}
}
private Method getMatchingMethod(Method m) {
if (m.getName().length() > 4 && m.getName().startsWith("set")) {
String field = m.getName().substring(3, m.getName().length());
for (Method getter : getters) {
if (getter.getName().equals("get" + field)) {
return getter;
} else if (getter.getName().equals("is" + field)) {
return getter;
}
}
} else if ((m.getName().length() > 4 && m.getName().startsWith("get")) || (m.getName().length() > 4
&& m.getName().startsWith("is"))) {
String name = m.getName();
int offset;
offset = name.startsWith("get") ? 3 : 2;
String field = name.substring(offset, name.length());
for (Method setter : setters) {
if (setter.getName().equals("set" + field)) {
return setter;
}
}
}
return null;
}
/**
* Called when the unit is reset. Restores the unit's variables to values
* specified by the parameters.
*/
public void reset() {
// TODO
}
/**
* Called when the unit is disposed of.
*/
public void dispose() {
// Insert code to clean-up NewInstance (e.g. close open files)
}
/**
* Called a parameters is updated (e.g. by the GUI)
*/
public void parameterUpdate(String paramname, Object value) {
if (paramname.equals("selectedMethod")) {
String val = (String) value;
for (Method setter : setters) {
if (setter.getName().equals(val)) {
selectedMethod = setter;
setIOTypes(selectedMethod);
try {
getTask().setSubTitle(selectedMethod.getName());
updateInputNodes();
updateOutputNodes();
} catch (NodeException e) {
e.printStackTrace();
}
break;
}
}
} else if (paramname.equals("value")) {
this.primitiveValue = (String) value;
}
}
private void setIOTypes(Method selectedMethod) {
List<String> ins = new ArrayList<String>();
Class[] params = selectedMethod.getParameterTypes();
for (Class param : params) {
ins.add(param.getName());
}
currentInputs = ins.toArray(new String[ins.size()]);
Method getter = getMatchingMethod(selectedMethod);
if (getter != null) {
Class output = getter.getReturnType();
if (!output.equals(void.class)) {
currentOutputs = new String[]{output.getName()};
}
}
}
/**
* todo - mesh types
*
* @throws NodeException
*/
private void updateInputNodes() throws NodeException {
Node[] ins = getTask().getDataInputNodes();
for (int i = ins.length - 1; i >= 0; i--) {
Node n = ins[i];
getTask().removeDataInputNode(n);
}
for (String currentInput : currentInputs) {
getTask().addDataInputNode();
}
}
private void updateOutputNodes() throws NodeException {
Node[] ns = getTask().getDataOutputNodes();
for (int i = ns.length - 1; i >= 0; i--) {
Node n = ns[i];
getTask().removeDataOutputNode(n);
}
for (String current : currentOutputs) {
getTask().addDataOutputNode();
}
}
/**
* @return an array of the input types accepted by nodes not covered
* by getNodeInputTypes().
*/
public String[] getInputTypes() {
for (String s : currentInputs) {
log("input type:" + s);
}
return currentInputs;
}
/**
* @return an array of the input types output by nodes not covered
* by getNodeOutputTypes().
*/
public String[] getOutputTypes() {
for (String s : currentOutputs) {
log("output type:" + s);
}
return currentOutputs;
}
public boolean isArray(Class cls) {
return cls.isArray();
}
public boolean isCollection(Class cls) {
return Collection.class.isAssignableFrom(cls);
}
public boolean isArrayOrCollection(Class cls) {
return isArray(cls) || isCollection(cls);
}
public static boolean isPrimitive(Class cls) {
return cls == Boolean.TYPE ||
cls == Byte.TYPE ||
cls == Short.TYPE ||
cls == Character.TYPE ||
cls == Integer.TYPE ||
cls == Long.TYPE ||
cls == Double.TYPE ||
cls == Float.TYPE;
}
public static boolean isWrapper(Class cls) {
return cls.equals(Boolean.class) ||
cls.equals(Byte.class) ||
cls.equals(Short.class) ||
cls.equals(Character.class) ||
cls.equals(Integer.class) ||
cls.equals(Long.class) ||
cls.equals(Double.class) ||
cls.equals(Float.class);
}
public void setValue(String value) {
if (isWrapper(beanClass)) {
try {
Constructor c = beanClass.getConstructor(String.class);
bean = c.newInstance(value);
} catch (Exception e) {
e.printStackTrace();
try {
Constructor c = beanClass.getConstructor(char.class);
bean = c.newInstance(value.charAt(0));
} catch (Exception e1) {
e1.printStackTrace();
}
}
} else {
if (beanClass.equals(String.class)) {
bean = value;
} else if (isEnum(beanClass)) {
bean = Enum.valueOf(beanClass, value);
}
}
}
public static boolean isEnum(Class cls) {
return Enum.class.isAssignableFrom(cls);
}
public static boolean isPrimitiveOrWrapperOrEnum(Class cls) {
return isPrimitive(cls) || isWrapper(cls) || isEnum(cls);
}
public static Object wrapPrimitive(boolean val) {
return new Boolean(val);
}
public static Object wrapPrimitive(int val) {
return new Integer(val);
}
public static Object wrapPrimitive(byte val) {
return new Byte(val);
}
public static Object wrapPrimitive(short val) {
return new Short(val);
}
public static Object wrapPrimitive(long val) {
return new Long(val);
}
public static Object wrapPrimitive(char val) {
return new Character(val);
}
public static Object wrapPrimitive(double val) {
return new Double(val);
}
public static Object wrapPrimitive(float val) {
return new Float(val);
}
public static Object wrapPrimitive(String val) {
if (val.equals("boolean")) {
return wrapPrimitive(false);
} else if (val.equals("byte")) {
byte x = 0;
return wrapPrimitive(x);
} else if (val.equals("short")) {
short x = 0;
return wrapPrimitive(x);
} else if (val.equals("char")) {
char x = 0;
return wrapPrimitive(x);
} else if (val.equals("long")) {
long x = 0L;
return wrapPrimitive(x);
} else if (val.equals("double")) {
double x = 0.0;
return wrapPrimitive(x);
} else if (val.equals("float")) {
float x = 0.0F;
return wrapPrimitive(x);
} else {
int x = 0;
return wrapPrimitive(x);
}
}
public static Class wrapPrimitiveClass(Class cls) {
if (cls.equals(boolean.class)) {
return Boolean.class;
} else if (cls.equals(byte.class)) {
return Byte.class;
} else if (cls.equals(int.class)) {
return Integer.class;
} else if (cls.equals(char.class)) {
return Character.class;
} else if (cls.equals(long.class)) {
return Long.class;
} else if (cls.equals(double.class)) {
return Double.class;
} else if (cls.equals(short.class)) {
return Short.class;
} else {
return cls;
}
}
}