oms3.ComponentAccess Maven / Gradle / Ivy
/*
* $Id: ComponentProxy.java 20 2008-07-25 22:31:07Z od $
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
package oms3;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import oms3.annotations.Bound;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Out;
import oms3.annotations.Range;
import oms3.annotations.Role;
import oms3.gen.MethodInvoker;
import oms3.util.Annotations;
/**
* Component Access.
*
* This class manages reflective access to components internals for the
* purpose of their integration into a model.
*
* @author od ([email protected])
* @version $Id: ComponentProxy.java 20 2008-07-25 22:31:07Z od $
*/
public class ComponentAccess {
private static final Logger log = Logger.getLogger("oms3.sim");
/** target component */
Object comp;
// in and out fields
// name->fieldaccess
Map ins = new LinkedHashMap();
Map outs = new LinkedHashMap();
// execution notification.
Notification ens;
/** Execute method. */
final MethodInvoker exec;
// public static int counter;
// static final Object lock = new Object();
// static void inc() {
// synchronized (lock) {
// counter++;
// }
// }
public ComponentAccess(Object cmd) {
this(cmd, null);
}
ComponentAccess(Object comp, Notification ens) {
this.comp = comp;
this.ens = ens;
Method execute = getMethodOfInterest(comp, Execute.class);
exec = Utils.reflective(comp, execute);
// exec = Utils.compiled(comp, execute);
findAll(comp, ins, outs, ens);
}
/**
* Get the component that is wrapped in this access proxy
*
* @return the component
*/
public Object getComponent() {
return comp;
}
void setInput(String name, Access fa) {
ins.put(name, fa);
}
void setOutput(String name, Access fa) {
outs.put(name, fa);
}
/**
* Get the all the inputs.
* @return list of input field access objects
*/
public Collection inputs() {
return ins.values();
}
/**
* Get the all the outputs.
* @return list of output field assess objects
*/
public Collection outputs() {
return outs.values();
}
/**
* Get a single input field.
*
* @param field the name of the field
* @return the input access object
*/
public Access input(String field) {
return ins.get(field);
}
/**
* get a single output field.
* @param field
* @return the output Field access object
*/
public Access output(String field) {
return outs.get(field);
}
final void exec() throws ComponentException {
try {
ens.fireWait(this);
// synchonized in()
for (Access a : ins.values()) { // wait for all inputs to arrive
if (a.getClass() == FieldAccess.class) {
a.in();
}
}
// un synchonized in()
for (Access a : ins.values()) { // wait for all inputs to arrive
if (a.getClass() == FieldObjectAccess.class || a.getClass() == FieldValueAccess.class
|| a.getClass() == AsyncFieldAccess.class) {
a.in(); // not synchonized.
}
}
ens.fireStart(this);
exec.invoke(); // execute the object's exec method
ens.fireFinnish(this);
// unsynchronized out
for (Access a : outs.values()) { // notify for output.
if (a.getClass() == FieldObjectAccess.class || a.getClass() == AsyncFieldAccess.class) {
a.out();
}
}
// synchronized out
for (Access a : outs.values()) { // notify for output.
if (a.getClass() == FieldAccess.class) {
a.out();
}
}
} catch (InvocationTargetException ex) {
throw new ComponentException(ex.getCause(), comp);
} catch (Exception ex) {
throw new ComponentException(ex, comp);
}
}
void callAnnotatedMethod(Class extends Annotation> ann, boolean lazy) {
try {
getMethodOfInterest(comp, ann).invoke(comp);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (IllegalArgumentException ex) {
if (!lazy) {
throw new RuntimeException(ex.getMessage());
}
}
}
/** Find the execute method.
* @Execute or execute() will match in this order.
*
* @param cmp the Object where to look for
* @return the method
*/
@SuppressWarnings("unchecked")
private static Method getMethodOfInterest(Object cmp, Class extends Annotation> ann) {
Class cmpClass = cmp.getClass();
Class infoClass = infoClass(cmpClass);
Method[] ms = infoClass.getMethods();
for (Method m : ms) {
if (m.getAnnotation(ann) != null) {
if (m.getReturnType() != Void.TYPE || m.getParameterTypes().length > 0) {
throw new IllegalArgumentException("Invalid Method signature: " + m);
}
try {
return cmpClass.getMethod(m.getName(), m.getParameterTypes());
} catch (Exception ex) {
throw new ComponentException("Cannot find/access method: " + m);
}
}
}
throw new IllegalArgumentException("No " + ann.getCanonicalName() + " found in " + cmp.getClass());
}
private static void findAll(Object cmp, Map ins, Map outs, Notification ens) {
Class cmpClass = cmp.getClass();
Class infoClass = infoClass(cmpClass);
for (Field f : infoClass.getFields()) {
try {
if (f.getAnnotation(In.class) != null) {
ins.put(f.getName(), new FieldAccess(cmp, cmpClass.getField(f.getName()), ens));
// ins.put(f.getName(), new FieldAccess(cmp, f, ens));
}
if (f.getAnnotation(Out.class) != null) {
outs.put(f.getName(), new FieldAccess(cmp, cmpClass.getField(f.getName()), ens));
// outs.put(f.getName(), new FieldAccess(cmp, f, ens));
}
} catch (Exception ex) {
throw new ComponentException("Cannot find/access field: " + f);
}
}
}
/// static helper methods
/** Call an method by Annotation.
*
* @param o the object to call.
* @param ann the annotation
* @param lazy if true, the a missing annotation is OK. if false
* the annotation has to be present or a Runtime exception is thrown.
*/
public static void callAnnotated(Object o, Class extends Annotation> ann, boolean lazy) {
try {
getMethodOfInterest(o, ann).invoke(o);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (IllegalArgumentException ex) {
if (!lazy) {
throw new RuntimeException(ex.getMessage());
}
}
}
/**
* Get the info class for a component object
* @param cmp
* @return the class that contains the annotations.
*/
public static Class infoClass(Class cmp) {
Class infoClass = null;
try {
infoClass = Class.forName(cmp.getName() + "CompInfo");
} catch (ClassNotFoundException E) { // there is no info class,
infoClass = cmp;
}
return infoClass;
}
/**
* Adjust the output path.
*
* @param outputDir
* @param comp
* @param log
* @return true is adjusted, false otherwise.
*/
public static boolean adjustOutputPath(File outputDir, Object comp, Logger log) {
boolean adjusted = false;
ComponentAccess cp = new ComponentAccess(comp);
for (Access in : cp.inputs()) {
String fieldName = in.getField().getName();
Class fieldType = in.getField().getType();
if (fieldType == File.class) {
Role role = in.getField().getAnnotation(Role.class);
if (role != null && Annotations.plays(role, Role.OUTPUT)) {
try {
File f = (File) in.getField().get(comp);
if (f != null && !f.isAbsolute()) {
f = new File(outputDir, f.getName());
in.setFieldValue(f);
adjusted = true;
if (log.isLoggable(Level.CONFIG)) {
log.config("Adjusting output for '" + fieldName + "' to " + f);
}
}
} catch (Exception ex) {
throw new ComponentException("Failed adjusting output path for '" + fieldName);
}
}
}
}
return adjusted;
}
/** Create a default parameter set
*
* @param comp
* @return the default properties
*/
public static Properties createDefault(Object comp) {
Properties p = new Properties();
ComponentAccess ca = new ComponentAccess(comp);
// over all input slots.
for (Access in : ca.inputs()) {
try {
String name = in.getField().getName();
Object o = in.getField().get(comp);
if (o != null) {
String value = o.toString();
p.put(name, value);
}
} catch (Exception ex) {
throw new ComponentException("Failed access to field: " + in.getField().getName());
}
}
return p;
}
static Map convert(Map m) {
LinkedHashMap hm = new LinkedHashMap();
Set> entrySet = m.entrySet();
for (Entry entry : entrySet) {
String key = entry.getKey();
key = key.replace('.', '_');
Object value = entry.getValue();
hm.put(key, value);
}
return hm;
}
public static Object conv(Object inpValue, Class> fieldType) {
Class inpType = inpValue.getClass();
if (inpType == String.class && fieldType != String.class) {
inpValue = Conversions.convert((String) inpValue, fieldType);
} else if (inpType == BigDecimal.class && fieldType != BigDecimal.class) {
inpValue = Conversions.convert(inpValue.toString(), fieldType);
} else if (inpValue instanceof CharSequence) {
inpValue = Conversions.convert(inpValue.toString(), fieldType);
}
return inpValue;
}
/**
* Set the input data as map.
* @param inp
* @param comp
* @param log
*/
@SuppressWarnings("unchecked")
public static boolean setInputData(Map inp, Object comp, Logger log) {
PrintWriter w = null;
File file = null;
boolean success = true;
ComponentAccess cp = new ComponentAccess(comp);
inp = convert(inp);
for (Access in : cp.inputs()) {
String fieldName = in.getField().getName();
Class fieldType = in.getField().getType();
Object inpValue = inp.get(fieldName);
if (inpValue != null) {
// allow files and dates provided as strings and
// convert them
try {
inpValue = conv(inpValue, fieldType);
// check the range if possible.
if (Number.class.isAssignableFrom(fieldType) || fieldType == double.class || fieldType == float.class) {
Range range = in.getField().getAnnotation(Range.class);
if (range != null) {
double v = ((Number) inpValue).doubleValue();
if (!Annotations.inRange(range, v)) {
if (log.isLoggable(Level.WARNING)) {
log.warning("Value '" + v + "' not in Range: " + range);
}
}
}
}
in.setFieldValue(inpValue);
if (log.isLoggable(Level.CONFIG)) {
log.config("@In " + comp.getClass().getName() + "@" + fieldName + " <- '" + inpValue + "'");
}
} catch (Exception ex) {
throw new ComponentException("Failed setting '" + fieldName + "' type " + in.getField().getType() + " <- " + ex.getMessage());
}
continue;
} else {
if (System.getProperty("oms3.check_params") != null) {
try {
if (w == null) {
file = new File(System.getProperty("oms3.work", System.getProperty("user.dir")), "missing_params.csv");
w = new PrintWriter(new FileWriter(file));
w.println("# Missing parameter, copy those entries into one of your parameter files.");
}
String val = null;
Bound b = null;
Object o = in.getFieldValue();
if (o != null) {
val = o.toString();
} else {
b = in.getField().getAnnotation(Bound.class);
if (b != null) {
try {
Object v = inp.get(b.value());
if (v == null) {
v = new Integer(0);
}
int dim = Integer.parseInt(v.toString());
int[] d = new int[dim];
val = Conversions.convert(d, String.class);
} catch (NumberFormatException E) {
val = "?";
}
} else {
val = "?";
}
}
w.println("@P, " + fieldName + ", \"" + val + "\"");
if (b != null) {
w.println(" bound, " + b.value());
}
} catch (Exception E) {
throw new RuntimeException(E);
}
}
if (log.isLoggable(Level.WARNING)) {
log.warning("No Input for '" + fieldName + "'");
}
}
}
if (w != null) {
w.close();
System.out.println("Missing parameter [" + file + "]");
success = false;
}
return success;
}
public static String dump(Object comp) throws Exception {
StringBuilder b = new StringBuilder();
b.append("// In\n");
ComponentAccess cp = new ComponentAccess(comp);
for (Access in : cp.inputs()) {
String name = in.getField().getName();
Object val = in.getFieldValue();
b.append(" " + name + ": " + Conversions.convert(val, String.class) + "\n");
}
b.append("// Out\n");
for (Access in : cp.outputs()) {
String name = in.getField().getName();
Object val = in.getFieldValue();
b.append(" " + name + ": " + Conversions.convert(val, String.class) + "\n");
}
b.append("\n");
return b.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy