oms3.ComponentAccess Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oms Show documentation
Show all versions of oms Show documentation
Object Modeling System (OMS) is a pure Java object-oriented framework.
OMS v3.+ is a highly interoperable and lightweight modeling framework for component-based model and simulation development on multiple platforms.
/*
* $Id: ComponentAccess.java 7cba5ba59d73 2018-11-29 [email protected] $
*
* This file is part of the Object Modeling System (OMS),
* 2007-2012, Olaf David and others, Colorado State University.
*
* OMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1.
*
* OMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OMS. If not, see .
*/
package oms3;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Out;
import oms3.annotations.Range;
import oms3.annotations.Role;
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])
*
*/
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 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.methodInvoker(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 the name of the 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());
} 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));
}
if (f.getAnnotation(Out.class) != null) {
outs.put(f.getName(), new FieldAccess(cmp, cmpClass.getField(f.getName()), 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 the component object
* @return the class that contains the annotations.
*/
public static Class infoClass(Class cmp) {
Class info = null;
try {
info = Class.forName(cmp.getName() + "CompInfo");
} catch (ClassNotFoundException E) { // there is no info class,
info = cmp;
}
return info;
}
/**
* Adjust the output path.
*
* @param outputDir the output directory
* @param comp the component object
* @param log the logger
* @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 the component object
* @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;
}
public static String dump(Object comp) throws Exception {
StringBuilder b = new StringBuilder();
b.append("//" + comp.toString() + ":\n");
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();
}
public static void rangeCheck(Object comp, boolean in, boolean out) throws Exception {
ComponentAccess cp = new ComponentAccess(comp);
Collection acc = new ArrayList();
if (in) {
acc.addAll(cp.inputs());
}
if (out) {
acc.addAll(cp.outputs());
}
for (Access a : acc) {
String name = a.getField().getName();
Object val = a.getFieldValue();
Range range = a.getField().getAnnotation(Range.class);
if (range != null) {
if (val instanceof Number) {
double v = ((Number) val).doubleValue();
if (!Annotations.inRange(range, v)) {
throw new ComponentException(name + " not in range " + v);
}
} else if (val instanceof double[]) {
double[] v = (double[]) val;
for (int i = 0; i < v.length; i++) {
if (!Annotations.inRange(range, v[i])) {
throw new ComponentException(name + " not in range " + v[i]);
}
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy