View Javadoc

1   package cz.cuni.amis.pogamut.sposh.executor;
2   
3   import java.lang.reflect.InvocationTargetException;
4   import java.lang.reflect.Method;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   
9   import cz.cuni.amis.pogamut.sposh.JavaBehaviour;
10  import cz.cuni.amis.pogamut.sposh.SPOSHAction;
11  import cz.cuni.amis.pogamut.sposh.SPOSHSense;
12  import cz.cuni.amis.pogamut.sposh.engine.PoshEngine;
13  import cz.cuni.amis.pogamut.sposh.engine.VariableContext;
14  
15  /**
16   * Executor for posh plans that will execute annotated methods on the object.
17   * <p/>
18   * You put behavior objects into this {@link IWorkExecutor} that have some methods
19   * annotated with {@link SPOSHAction} or {@link SPOSHSense}. Names of these methods
20   * are used stored as primitives. There can't be two behavior methods with same
21   * names, (e.g. testMethod() from behavior object A and testMethod() from object B or
22   * testMethod(String) from object A). If such sitation occurs,
23   * {@link BehaviorWorkExecutor#addBehavior(java.lang.Object) } will throw
24   * {@link IllegalArgumentException}.
25   * <p/>
26   * When {@link PoshEngine} asks to execute some primitive, this executor will look 
27   * in list of primitives it is capable to execute (names of annotated methods 
28   * from behavior objects) and if it has some behavior method with same name, it executed it
29   * and returns value.
30   *
31   * @author Honza
32   */
33  public class BehaviorWorkExecutor implements ILogicWorkExecutor {
34  
35      /**
36       * Class for storing methods that can be executed as primitives.
37       */
38      protected class BehaviorMethod {
39          /**
40           * Create new behavior method. 
41           */
42          public BehaviorMethod(Object behavior, Method method) {
43              this.behavior = behavior;
44              this.method = method;
45          }
46          public final Object behavior;
47          public final Method method;
48      }
49  
50      /**
51       * Map that maps name of primitives into behavior methods.
52       */
53      protected final HashMap<String, BehaviorMethod> primitives = new HashMap<String, BehaviorMethod>();
54      
55      protected List<JavaBehaviour> behaviors = new ArrayList<JavaBehaviour>();
56  
57      /**
58       * Create BehaviorWorkExecutor with no methods.
59       */
60      public BehaviorWorkExecutor() {
61      }
62      
63      /**
64       * Create BehaviorWorkExecutor with primitives from behavior.
65       * @param  behavior object from which we will add primitives into this worker.
66       */
67      public BehaviorWorkExecutor(JavaBehaviour behavior) {
68          addBehavior(behavior);
69      }
70  
71      /**
72       * Take the behavior object, find its methods annotated with either
73       * {@link SPOSHAction} or {@link SPOSHSense} and add them as primitives this
74       * work executor can process.
75       * @param behavior
76       * @throws IllegalArgumentException if behavior contains primitive method with same name, that is
77       * already contained in primitives of this worker.
78       */
79      public synchronized void addBehavior(JavaBehaviour behavior) {
80      	behaviors.add(behavior);
81          Method[] methods = behavior.getClass().getMethods();
82  
83          // filter methods to only
84          for (Method method : methods) {
85              boolean isAnnotated =
86                  method.isAnnotationPresent(SPOSHAction.class) ||
87                  method.isAnnotationPresent(SPOSHSense.class);
88  
89              String name = method.getName();
90              if (isAnnotated) {
91                  if (primitives.containsKey(name)) {
92                      throw new IllegalArgumentException("primitive name clash (there are at least 2 primitives with name \"" + name + "\")");
93                  }
94                  primitives.put(name, new BehaviorMethod(behavior, method));
95              }
96          }
97      }
98  
99  
100     /**
101      * Execute the primitive. Take behavior that contains method with same name
102      * as primitive and execute it, if method has as first parameter {@link VariableContext},
103      * pass it, the otherwise all parameters passed to method will be null.
104      *
105      * @param primitive
106      * @param ctx
107      * @return
108      * @throws IllegalArgumentException if there is no behavior method with same name as primitive
109      */
110     private Object executePrimitive(String primitive, VariableContext ctx) {
111         BehaviorMethod behaviorMethod = primitives.get(primitive);
112         if (behaviorMethod == null) {
113             throw new IllegalArgumentException("Primitive \"" + primitive + "\" has no behavior method.");
114         }
115 
116         Method method = behaviorMethod.method;
117 
118         // create and fill arguments for behavior method
119         Object[] args = new Object[method.getParameterTypes().length];
120         if (method.getParameterTypes().length > 0) {
121             Class firstParameterClass = method.getParameterTypes()[0];
122 
123             if (firstParameterClass.equals(VariableContext.class)) {
124                 args[0] = ctx;
125             }
126         }
127         // Try to invoke behavior method, so many things can go wrong...
128         try {
129             return method.invoke(behaviorMethod.behavior, args);
130         } catch (IllegalAccessException ex) {
131             // Wrong access for method specified.
132             throw new RuntimeException(ex);
133         } catch (IllegalArgumentException ex) {
134             // Some things in arguments were wrong
135             throw new RuntimeException(ex);
136         } catch (InvocationTargetException ex) {
137             // if underlaying method invoked exception
138             throw new RuntimeException(ex.getCause());
139         }
140     }
141     
142     /**
143      * Uses {@link BehaviorWorkExecutor#executePrimitive(String, VariableContext)}.
144      */
145     @Override
146     public Object executeSense(String primitive, VariableContext ctx) {
147         return executePrimitive(primitive, ctx);
148     }
149     
150     /**
151      * Uses {@link BehaviorWorkExecutor#executePrimitive(String, VariableContext)}.
152      */
153     @Override
154     public ActionResult executeAction(String primitive, VariableContext ctx) {
155     	return (ActionResult) executePrimitive(primitive, ctx);
156     }
157 
158 	@Override
159 	public void logicAfterPlan() {
160 		for (JavaBehaviour behavior : behaviors) {
161 			behavior.logicAfterPlan();
162 		}
163 	}
164 
165 	@Override
166 	public void logicBeforePlan() {
167 		for (JavaBehaviour behavior : behaviors) {
168 			behavior.logicBeforePlan();
169 		}
170 	}
171 
172 }