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 }