
simkit.SimEntityBaseProtected Maven / Gradle / Ivy
package simkit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import simkit.util.LinkedHashMap2;
/**
* A modified version of SimEntityBase that allows the use of protected
* event methods. Warning: Currently a prototype.
*
* @author Arnold Buss
* @author K. A. Stork
* @author John Ruck, R&A
* @version $Id$
*
**/
public abstract class SimEntityBaseProtected extends BasicSimEntity {
public static final Logger log = Logger.getLogger("simkit");
/**
* A two dimensional Hash table used to cache doMethods
* for all SimEntityBases. Keyed by Class and Method.
**/
private static LinkedHashMap2 allDoMethods;
/**
* A two dimensional Hash table used to hold the
* names and signatures of all doMethods of all SimEntityBases.
* Keyed by Class and Method name.
**/
private static LinkedHashMap2[]>>
allNamesAndSignatures;
/**
* True if the entity has a doRun method.
*/
protected boolean reRunnable = false;
/**
* True if the entity has a doRun method. Overrides BasicSimEntity to
* allow the doRun method to be protected or private.
*/
public boolean isReRunnable() {
return reRunnable;
}
static {
allDoMethods = new LinkedHashMap2();
allNamesAndSignatures = new LinkedHashMap2[]>>();
}
/**
* If true, print debug information
**/
private static boolean debug = false;
/**
* Contruct a new SimEntityBaseProtected with the given name and
* event priority.
* @param name The name of the entity.
* @param priority The default priority for processing this entity's events.
**/
public SimEntityBaseProtected(String name, Priority priority) {
super(name, priority);
log.warning("??? Warning: SimEntityBaseProtected is still a prototype and may not work.");
Map doMethods = allDoMethods.get(this.getClass());
if (doMethods == null) {
doMethods = new LinkedHashMap();
Method[] methods = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith(EVENT_METHOD_PREFIX)) {
doMethods.put(getFullMethodName(methods[i]), methods[i]);
String methodName = methods[i].getName();
} // if
} // for
allDoMethods.put(this.getClass(), doMethods);
} // if
Map[]>> namesAndSignatures = allNamesAndSignatures.get(this.getClass());
if (namesAndSignatures == null) {
namesAndSignatures = new LinkedHashMap[]>>();
for (Method nextDoMethod : doMethods.values()) {
List[]> v = null;
if (namesAndSignatures.containsKey(nextDoMethod.getName())) {
v = namesAndSignatures.get(nextDoMethod.getName());
}
else {
v = new ArrayList[]>();
namesAndSignatures.put(nextDoMethod.getName(), v);
}
v.add(nextDoMethod.getParameterTypes());
}
allNamesAndSignatures.put(this.getClass(), namesAndSignatures);
}
}
/**
* Construct a new SimEntityBaseProtected with a default name and priority.
* The name is the class name plus a unique serial number.
**/
public SimEntityBaseProtected() {
this(DEFAULT_ENTITY_NAME, DEFAULT_PRIORITY);
setName(getClass().getSimpleName() + '.' + getSerial());
}
/**
* Construct a new SimEntityBaseProtected with given name and a default priority.
* @param name The name of the entity.
**/
public SimEntityBaseProtected(String name) {
this(name, DEFAULT_PRIORITY);
}
/**
* Construct a new SimEntityBaseProtected with a default name and
* the given priority.
* The name is the class name plus a unique serial number.
* @param priority The priority for processing this entity's events.
**/
public SimEntityBaseProtected(Priority priority) {
this(DEFAULT_ENTITY_NAME, priority);
setName(getClass().getSimpleName() + '.' + getSerial());
}
/**
* Gets all the do Methods for this entity. This includes public, private, and
* protected events defined anywhere in the inheritence.
*/
protected Map getDoMethods() {
Map doMethods = new LinkedHashMap();
// I really wanted to handle this with recursion, but couldn't.
// Walk up the inheritance adding any method+signatures we haven't added yet.
// When getClass() is called on Object we get a null and stop.
Class clazz = this.getClass();
while (clazz != null) {
if (debug) System.out.println("+++ Checking class: " + clazz.getName() + " for do methods");
Method[] methods = clazz.getDeclaredMethods(); //Get methods declared in this class.
for (int i = 0; i < methods.length; i++) {
String fullName = getFullMethodName(methods[i], true); //name + wrapped signature.
if (debug) System.out.print("\tFound: " + fullName + "...");
// If it is a do method and has not been added then add it.
if (fullName.startsWith(EVENT_METHOD_PREFIX) && !doMethods.containsKey(fullName)) {
doMethods.put(fullName,methods[i]);
if (methods[i].getName().equals("doRun")) {
reRunnable = true;
if (debug) System.out.print("Found a doRun, setting reRunnable to true ... ");
} //endif doRun.
if (debug) System.out.println("*** added it***");
} else {
if (debug) System.out.println("did not add");
}// if fullName ...
} //for
clazz = clazz.getSuperclass();
}//while
return doMethods;
}
/**
* Process the given SimEvent. If the Method signature does not match any for
* this entity, the event is ignored. Also other entity's doRun events are ignored.
* Just calls processSimEvent.
*/
public synchronized void handleSimEvent(SimEvent event) {
processSimEvent(event);
}
/**
* Process the given SimEvent. If the Method signature does not match any for
* this entity, the event is ignored. Also other entity's doRun events are ignored.
*/
public synchronized void processSimEvent(SimEvent event) {
if (event == null) { return; }
Method m = null;
String methodName = event.getMethodName();
// Do not process other SimEntityBase's "doRun()" methods
if ( !event.getSource().equals(this) && event.getFullMethodName().equals("doRun()")) {
return;
} // if
// If no method of that name, then there is no hope.
Map[]>> namesAndSignatures = allNamesAndSignatures.get(this.getClass());
if (!namesAndSignatures.containsKey(methodName)) {
if (debug) {
System.out.println("No method of name " + methodName + " -- giving up...");
} // if
return;
} // if
if (isVerbose()) {
System.out.println("Event processed by " + this + ": " + event);
} // if
try {
Map doMethods = allDoMethods.get(this.getClass());
// This method has either happened before or matches one in method exactly
if (isDebug()) {
System.out.println("doMethods hashcode = " + doMethods.hashCode());
System.out.println("namesAndSignatures hashcode = " + namesAndSignatures.hashCode());
System.out.println("doMethods: " + doMethods);
}
if (doMethods.containsKey(event.getFullMethodName())) {
m = doMethods.get(event.getFullMethodName());
m.setAccessible(true);
m.invoke(this, event.getParameters());
// updateEventCounts(event);
} // if
else {
if (isVerbose()) {
System.out.println(
"Master lookup failed, trying namesAndSignatures..." +
" Method Name = " + event.getFullMethodName());
}
// Now, we are here only because there is some chance that there will be a match.
// First
Object[] params = event.getParameters();
for (Iterator[]> iter = namesAndSignatures.get(methodName).iterator(); iter.hasNext(); ) {
if (isDebug()) {
System.out.println("namesAndSignatures: " + namesAndSignatures.hashCode());
}
Class>[] signature = iter.next();
if (debug) {
System.out.print(" Signature: (");
for (int k = 0; k < signature.length; k++) {
System.out.print(signature[k]);
if (k < signature.length - 1) {System.out.print(", ");}
} // for
System.out.println(")");
} // if
if (signature.length == params.length) {
boolean match = true;
if (debug) {
System.out.println("There are " + signature.length + " arguments to check...");
} // if
for (int i = 0; i < signature.length; i++) {
if (debug) {
System.out.println("\tChecking: " + signature[i]);
} // if
if (signature[i].isPrimitive()) {
if (signature[i].equals(Float.TYPE)) {
match &= params[i].getClass().equals(java.lang.Float.class);
} // if
if (signature[i].equals(Integer.TYPE)) {
match &= params[i].getClass().equals(java.lang.Integer.class);
} // if
if (signature[i].equals(Double.TYPE)) {
match &= params[i].getClass().equals(java.lang.Double.class);
} // if
if (signature[i].equals(Long.TYPE)) {
match &= params[i].getClass().equals(java.lang.Long.class);
} // if
if (signature[i].equals(Boolean.TYPE)) {
match &= params[i].getClass().equals(java.lang.Boolean.class);
} // if
if (signature[i].equals(Byte.TYPE)) {
match &= params[i].getClass().equals(java.lang.Byte.class);
} // if
if (signature[i].equals(Short.TYPE)) {
match &= params[i].getClass().equals(java.lang.Short.class);
} // if
if (signature[i].equals(Character.TYPE)) {
match &= params[i].getClass().equals(java.lang.Character.class);
} // if
} // if
else {
match = match && ( params[i] == null || signature[i].isAssignableFrom(params[i].getClass()));
} // else
if (isVerbose()) {
System.out.println(signature[i].getName() + " ?=? " +
params[i].getClass().getName());
} // if
} // for
if (match) {
if (debug) {
System.out.println("Match found: " + event.getFullMethodName());
}
String key = getFullMethodName(methodName, signature, false);
m = doMethods.get(key);
if (m != null) {
m.setAccessible(true);
m.invoke(this, params);
doMethods.put(event.getFullMethodName(), m);
} else { // m is null
System.out.println("*** Error in SimEntityBaseProtected.ProcessSimEvent " +
"was unable to find " + key + " in the doMethods even though it was found earlier.");
}
} else {//no match
if (isVerbose()) {
System.out.println(" No match found\n");
}
}//endif match
} else { //param not same length
if (isVerbose()) {
System.out.println("Different number of parameters");
}
}//endif param lengths.
}
}
}
catch (NullPointerException e) {
System.err.println("\n*** In SimEntityBaseProtected.processSimEvent " + e);
System.err.println("Attempted method: " + event.getFullMethodName());
//e.printStackTrace();
throw(new RuntimeException(e));
}
catch(IllegalAccessException e) {
System.err.println("\n*** In SimEntityBaseProtected.processSimEvent " + e);
System.err.println("Attempted method: " + m );
System.err.println(" [key = " + event.getFullMethodName() +"]");
System.err.println(" [name = " + event.getMethodName() + "]");
System.err.print (" [params = (");
for (int i = 0; i < event.getParameters().length; i++) {
System.err.print(event.getParameters()[i].getClass().getName());
if (i < event.getParameters().length - 1) {System.out.print(",");}
}
System.err.println(") ]");
System.err.println("This object: " +
Integer.toHexString(this.hashCode()) );
System.err.print("This class: " );
Class c = this.getClass();
while(!c.equals(SimEntityBase.class)) {
System.err.print(c);
c = c.getSuperclass();
}
System.err.println(NL + "Method's object: " +
Integer.toHexString(event.getSource().hashCode()) );
System.err.println("Method's class: " + m.getDeclaringClass());
e.printStackTrace();
throw(new RuntimeException(e));
} //shouldn't happen
catch(IllegalArgumentException e) {
throw(new RuntimeException(e));
}
catch(InvocationTargetException e) {
throw(new RuntimeException(e));
}
finally {
}
}
/**
* Gets a String representation of this entity's event methods.
* This method is added by TRAC-WSMR, Authot Lt Col Olson, USMC.
* dumpDoMethodsStr
returns a String containing the same information as
* dumpDoMethods
. This method allows a developer to place the information
* in a graphical user interface, textbox, or similar output device.
*
*
Method rewritten by A. Buss to use newer Iterator (vice Enumeration) and
* to use StringBuffer
for speed. The underscores are also made to
* exactly match the length of the heading.
*
* @return String representation of this entity's "doMethods"
**/
public String dumpDoMethodsStr() {
String name = getName();
StringBuffer buf = new StringBuffer();
buf.append("Event Methods for ");
buf.append(name);
buf.append(NL);
for (int i = 0; i < 18 + name.length(); i++) {
buf.append('=');
}
buf.append(NL);
Map doMethods = (Map) allDoMethods.get(this.getClass());
for (Iterator i = doMethods.keySet().iterator(); i.hasNext(); ) {
buf.append(i.next());
buf.append(NL);
}
return buf.toString();
}
/**
* Prints out the "do" methods for this SimEntity
**/
public void dumpDoMethods() {
System.out.println(this.dumpDoMethodsStr());
}
/**
* Produces a String containing the names and signatures of this entity's "do" methods.
*
This method is added by TRAC-WSMR, Authot Lt Col Olson, USMC.
* dumpNamesAndSignaturesStr()
returns a String containing the same information as
* dumpNamesAndSignatures()
. This method allows a developer to place the information
* in a graphical user interface, textbox, or similar output device.
*
*
A. Buss modified this to utilize Iterators and StringBuffer. Also, the corresponding
* old method now simply invokes this one.
*
* @return String representation of this entity's "NamesAndSignatures"
*
**/
public String dumpNamesAndSignaturesStr() {
String name = getName();
StringBuffer buf = new StringBuffer();
buf.append("Names and signatures for ");
buf.append(name);
buf.append(NL);
for (int i = 0; i < 25 + name.length(); i++){
buf.append('=');
}
buf.append(NL);
Map namesAndSignatures = (Map) allNamesAndSignatures.get(this.getClass());
for (Iterator i = namesAndSignatures.keySet().iterator(); i.hasNext(); ) {
Object methodName = i.next();
buf.append(methodName);
buf.append(':');
buf.append('\t');
buf.append('(');
for (Iterator j = ((List)namesAndSignatures.get(methodName)).iterator(); j.hasNext();) {
Class[] aClass = (Class[]) j.next();
for (int k = 0; k < aClass.length; k++) {
buf.append(aClass[k].getName());
if (k < aClass.length - 1) {buf.append(',');}
}
buf.append(')');
if (i.hasNext()) {buf.append(NL);}
}
}
return buf.toString();
}
/**
* Prints names and signatures of "do" methods to output specified by Schedule.
**/
public void dumpNamesAndSignatures() {
String str = dumpNamesAndSignaturesStr();
System.out.println(str);
}
/**
* Interrupt all this SimEntity's events when a certain time is reached
* @deprecated - Use stopAtTime(double);
* @param endingTime The ending time at which all this SimEntity's events are interrupted.
**/
public void stopOnTime(double endingTime) {
this.stopAtTime(endingTime);
}
/**
* Interrupt all this SimEntity's events when a certain time is reached
* @param endingTime The ending time at which all this SimEntity's events are interrupted.
**/
public void stopAtTime(double endingTime) {
new Stop().waitDelay("StopSimEntity", endingTime, Priority.LOWEST, this);
}
/**
* If true, print debug information
**/
public static void setDebug(boolean b) {debug = b;}
/**
* If true, print debug information
**/
public static boolean isDebug() {return debug;}
/**
* Gets the method name plus signature as a String.
* @param m The method for which to get the full name (unfortunately the jdk does
* not appear to provide this particular String...to my knowledge).
* @return The full method name of m, including signature, as a String.
**/
public static String getFullMethodName(Method m) {
String name = m.toString();
return m.getName() + getSignatureString(m);
}
/**
* Gets the method name plus signature as a String.
* @param name The name of the method.
* @param parameters An array of Class objects that are the signature of the method.
* @param wrap If true, primatives will be converted into Objects.
**/
public static String getFullMethodName(String name, Class[] parameters, boolean wrap) {
StringBuffer buf = new StringBuffer();
buf.append(name);
buf.append('(');
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
if (parameters[i] != null) {
if (parameters[i].isPrimitive() && wrap) {
if (parameters[i].equals(Integer.TYPE)) {
buf.append("java.lang.Integer");
} else if (parameters[i].equals(Float.TYPE)) {
buf.append("java.lang.Float");
} else if (parameters[i].equals(Double.TYPE)) {
buf.append("java.lang.Double");
} else if (parameters[i].equals(Long.TYPE)) {
buf.append("java.lang.Long");
} else if (parameters[i].equals(Boolean.TYPE)) {
buf.append("java.lang.Boolean");
} else if (parameters[i].equals(Byte.TYPE)) {
buf.append("java.lang.Byte");
} else if (parameters[i].equals(Short.TYPE)) {
buf.append("java.lang.Short");
} else if (parameters[i].equals(Character.TYPE)) {
buf.append("java.lang.Character");
} else {
System.out.println("*** Error in SimEntityBaseProtected.getFullMethodNameWrapped ");
System.out.println("\tAn unknown primitive data type: " + parameters[i].getName());
}
} else {//else not Primitive
buf.append(parameters[i].getName());
} // endif Primative
}
else { //else paramters[i] null
buf.append("null");
}
if (i < parameters.length - 1) {buf.append(',');}
}//for
}//null
buf.append(')');
return buf.toString();
}
/**
* Gets the signature of the Method as a String, primative arguments are
* displayed with the primative name (e.g, int)
* @param m The method for which to get the signature as a String (unfortunately
* the jdk does not appear to provide this particular String either...to my knowledge).
* @return The signature m as a String.
**/
public static String getSignatureString(Method m) {
String name = m.toString();
return name.substring(name.indexOf('('));
}
/**
* Gets a String containing the Method name plus the signature, with any
* primative parameters optionally wrapped in an Object.
* @param m Get the name of this Method
* @param wrap If true wrap primitives in an Object. (e.g., "int" is replaced by
* "java.lang.Integer"
*/
public static String getFullMethodName(Method m, boolean wrap) {
if (wrap) {
Class[] parameters = m.getParameterTypes();
String name = m.getName();
return getFullMethodName(name, parameters, true);
}
return getFullMethodName(m); //un-wrapped
}
/**
* Determines if a event signature is equivelent to the given arguments.
* @param signature An array of the method's Classes.
* @param args An array containing the method's arguments as Objects.
* @return True if the corresponding signatures are assignable from
* the arguments and are therefore equivelent.
**/
public static boolean isAssignableFrom(Class>[] signature, Object[] args) {
boolean assignable = true;
if (signature.length != args.length) {
assignable = false;
}
else {
for (int i = 0; i < signature.length; i++) {
if (!signature[i].isAssignableFrom(args[i].getClass())) {
assignable = false;
break;
}
}
}
return assignable;
}
/**
* Clears cache of doMethods and namesAndSignatures
*/
public static void coldReset() {
allDoMethods.clear();
allNamesAndSignatures.clear();
}
}