View Javadoc

1   package cz.cuni.amis.pogamut.shady;
2   
3   import cz.cuni.amis.pogamut.sposh.engine.VariableContext;
4   import cz.cuni.amis.pogamut.sposh.exceptions.MissingRootException;
5   import cz.cuni.amis.pogamut.sposh.exceptions.NoEligibleElementException;
6   import cz.cuni.amis.pogamut.sposh.executor.IWorkExecutor;
7   import java.math.BigDecimal;
8   import java.util.Collections;
9   import java.util.List;
10  import java.util.SortedMap;
11  import java.util.TreeMap;
12  
13  /**
14   * This class is taking a shade plan (already parsed in form of {@link ShadeTree}
15   * and executing it using supplied {@link IWorkExecutor}.
16   *
17   * Shade engine is an if-then tree like structure, that is evaluated during
18   * every iteration. It is very simple, but it should be the advantage, not
19   * disadvantage. Posh engine is very complicated with many features very few
20   * know about, much less actually use.
21   *
22   * @author Honza
23   */
24  public class ShadeEngine<EXECUTOR extends IWorkExecutor> {
25  
26      private final ShadeTree plan;
27      private final EXECUTOR executor;
28  
29      /**
30       * Create new engine with set plan and executor.
31       * @param plan plan that this engine will execute.
32       * @param executor executor used to execute queries and actions.
33       */
34      public ShadeEngine(ShadeTree plan, EXECUTOR executor) {
35          this.plan = plan;
36          this.executor = executor;
37      }
38  
39      /**
40       * Take the plan evaluate it and run the target primitive.
41       * 
42       */
43      public void evaluate() throws MissingRootException, NoEligibleElementException {
44          ShadeNode node = plan.getRoot();
45  
46          NodeCall call = selectCall(node);
47  
48          // Ok, now we have call that is not another node
49      }
50  
51      /**
52       * Go throught the node tree and find the leaf ({@link NodeElement) that
53       * doesn't call another {@link ShadeNode node} and return the call of
54       * the leaf.
55       * @param node node where we start the search. Node must be part
56       *             of the {@link ShadeEngine#plan} (not checked).
57       * @return Found call
58       */
59      protected NodeCall selectCall(ShadeNode node) throws NoEligibleElementException {
60          // Get element with highest priority and enabled trigger
61          NodeElement elem = getSelectedElement(node);
62          NodeCall call = elem.getCall();
63          ShadeNode childNode = plan.findNode(call.getName());
64  
65          if (childNode != null) {
66              return selectCall(childNode);
67          }
68          return call;
69      }
70  
71      /**
72       * Take the node and find which of its elements should be selected
73       * according to highest priority and enabled trigger.
74       * @param node node for searched elements
75       * @return selected node
76       */
77      protected NodeElement getSelectedElement(ShadeNode node) throws NoEligibleElementException {
78          // sort by priority, from highest to the lowest
79          SortedMap<BigDecimal, NodeElement> elements =
80                  new TreeMap<BigDecimal, NodeElement>(Collections.reverseOrder());
81          for (NodeElement elem : node.getElements()) {
82              BigDecimal elementPriority = elem.getPriority().execute(executor);
83              elements.put(elementPriority, elem);
84          }
85  
86          // For every element according to descending priority check trigger
87          // and if it is satisfied, execute the call of the element.
88          for (NodeElement elem : elements.values()) {
89              BigDecimal triggerResult = elem.getTrigger().execute(executor);
90  
91              // if trigger evaluates true...
92              if (!BigDecimal.ZERO.equals(triggerResult)) {
93                  return elem;
94              }
95          }
96          throw new NoEligibleElementException("Node \"" + node.getName() + "\" doesn't have even one elegible element.");
97      }
98  
99      protected BigDecimal executeCall(NodeCall call) {
100         VariableContext ctx = new VariableContext();
101         List<IArgument> args = call.getArgs();
102 
103         for (int argIdx = 0; argIdx < args.size(); ++argIdx) {
104             ctx.put(Integer.toString(argIdx), args.get(argIdx).getValue());
105         }
106         Object result;
107         
108         // TODO: this would not work at all!!!
109         
110         try {
111         	result = executor.executeSense(call.getName(), ctx);
112         } catch (Exception e) {
113         	result = executor.executeAction(call.getName(), ctx);
114         }
115 
116         return (BigDecimal) result;
117     }
118 }
119 
120 /**
121  * Node call with list of parameters specified by the caller. This object is
122  * immutable.
123  * @author Honza
124  */
125 class CallContext {
126 
127     /**
128      * Specification of the call.
129      */
130     private final String name;
131     /**
132      * Variable context of the call. Basically list of arguments passed to
133      * the called node/function.
134      */
135     private final List<IArgument> ctx;
136 
137     /**
138      * Create new {@link CallContext}. It is done by taking
139      * the {@link NodeCall call} and current {@link CallContext}, and merging
140      * them together, because we need to replace variables 
141      * in the {@link NodeCall}.
142      * @param call what is being called
143      * @param ctx
144      */
145     public CallContext(NodeCall call, List<IArgument> ctx) {
146         this.name = call.getName();
147         this.ctx = ctx;
148     }
149 }