/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.engine; import java.lang.reflect.*; import sim.util.*; /** A Steppable which calls an underlying method using Java's reflection system. The underlying method can either have no arguments or have one argumen (SimState), and is specified by its name as a String. <p>You can use MethodStep to call methods on classes that aren't Steppables. For example, if you have an object <b>myObject</b> and wish to submit both its <b>foo</b> and <b>bar</b> methods (no arguments) to be called at various times when the Schedule sees fit, you might do this: <pre><tt> MyClass myObject = ... ; schedule.scheduleRepeating( ... , new MethodStep(myObject, "foo"), ...); schedule.scheduleRepeating( ... , new MethodStep(myObject, "bar"), ...); </tt></pre> <p>This will call the <b>foo()</b> method and <b>bar()</b> method to be called at the appropriate times on myObject. <p>You can also use MethodStep to pop up objects which aren't Steppables by their nature. For example: <pre><tt> JFrame myJFrame = ... ; schedule.schedule( ... , new MethodStep(myJFrame, "show"), ...); </tt></pre> <p>MethodStep can also be called on methods which expect to be passed in the Steppable's <b>SimState</b> argument. When the MethodStep is called, it passes its argument to them. You do this by adding a <b>true</b> to the constructor: <pre><tt> MyClass myObject = ... ; schedule.scheduleRepeating( ... , new MethodStep(myObject, "baz", true), ...); schedule.scheduleRepeating( ... , new MethodStep(myObject, "quux", true), ...); </tt></pre> <p>This will call the <b>baz(SimState)</b> method and <b>quux(SimState)</b> method to be called at the appropriate times on myObject. <p>This method is mostly to help in ease of porting: but it's not good Java. Reflection is slow and violates all sorts of design contracts -- generally speaking, you should use anonymous Steppables intead. For example, the examples above could have instead been written this way: <pre><tt> final MyClass myObject = ... ; final JFrame myJFrame = ... ; schedule.scheduleRepeating( ... , new Steppable() { public void step(SimState state) { myObject.foo() } }, ...); schedule.scheduleRepeating( ... , new Steppable() { public void step(SimState state) { myObject.foo() } }, ...); schedule.schedule( ... , new Steppable() { public void step(SimState state) { myJFrame.show() } }, ...); schedule.scheduleRepeating( ... , new Steppable() { public void step(SimState state) { myObject.baz(state) } }, ...); schedule.scheduleRepeating( ... , new Steppable() { public void step(SimState state) { myObject.quux(state) } }, ...); </tt></pre> */ public class MethodStep implements Steppable { private static final long serialVersionUID = 1; Method method; Object object; boolean passInSimState; public MethodStep(Object object, String methodName) { this(object,methodName,false); } public MethodStep(Object object, String methodName, boolean passInSimState) { this.object = object; this.passInSimState = passInSimState; if (object==null) { throw new NullPointerException("MethodStep asked to call the method " + methodName + (passInSimState ? "\"(SimState state)" : "\"") + " on a null object"); } try { if (passInSimState) this.method = object.getClass().getMethod(methodName, new Class[]{SimState.class}); else this.method = object.getClass().getMethod(methodName, new Class[]{}); } catch (NoSuchMethodException ex) // make runtime exception { throw new RuntimeException("Could not find a public method called \"" + methodName + (passInSimState ? "\"(SimState state)" : "\"") + " in the class " + object.getClass()); } catch (SecurityException ex) { throw new RuntimeException("Could not find a public method called \"" + methodName + (passInSimState ? "\"(SimState state)" : "\"") + " in the class " + object.getClass()); } } public void step(final SimState state) { try { if (passInSimState) method.invoke(object, new Object[] { state }); else method.invoke(object, (Object[]) null); } catch (IllegalAccessException ex) // make runtime exception { // generally should not happen -- the getMethod() method should only return public methods throw new RuntimeException("Could not find a public method called \"" + method + (passInSimState ? "\"(SimState state)" : "\"") + " in the class " + object.getClass()); } catch (IllegalArgumentException ex) { throw new RuntimeException("Could not find a public method called \"" + method + (passInSimState ? "\"(SimState state)" : "\"") + " in the class " + object.getClass()); } catch (InvocationTargetException ex) { /** At the moment, Java 1.3 cannot do causes, so we don't include this for compatability reasons. Maybe we'll uncomment it later. */ /* Throwable t = new RuntimeException("On calling \"" + methodName +"\" in the class " + object.getClass() + ", an Exception was raised in the called method."); t.initCause(ex.getCause()); throw t; */ throw new RuntimeException("On calling \"" + method + (passInSimState ? "\"(SimState state)" : "\"") + " in the class " + object.getClass() + ", an Exception was raised in the called method.", ex); } catch (NullPointerException ex) { // should not be able to occur -- we've already verified that object is not null. throw new NullPointerException("MethodStep asked to call the method " + method + (passInSimState ? "\"(SimState state)" : "\"") + " on a null object"); } // dont' catch (ExceptionInInitializerError ex) -- let it throw through like any error } }