001 package com.hammurapi.reasoning.impl;
002
003 import java.util.ArrayList;
004 import java.util.Collections;
005 import java.util.Comparator;
006 import java.util.Iterator;
007 import java.util.List;
008 import java.util.logging.Level;
009 import java.util.logging.Logger;
010
011 import org.codehaus.janino.CompileException;
012 import org.codehaus.janino.ExpressionEvaluator;
013 import org.codehaus.janino.Parser.ParseException;
014 import org.codehaus.janino.Scanner.ScanException;
015
016 import com.hammurapi.config.bootstrap.ConfigurationException;
017 import com.hammurapi.reasoning.ExceptionHandler;
018
019
020 public class ExpressionConditionNode<F> extends ConditionNode<F> {
021 private static final Logger logger = Logger.getLogger(ExpressionConditionNode.class.getName());
022
023 private boolean isFine = logger.isLoggable(Level.FINE);
024
025 private String condition;
026
027 public void setCondition(String condition) {
028 this.condition = condition;
029 }
030
031 private List<ConditionArgument> conditionArguments = new ArrayList<ConditionArgument>();
032
033 public void addConditionArgument(ConditionArgument conditionArgument) {
034 conditionArguments.add(conditionArgument);
035 }
036
037 @Override
038 public String toString() {
039 return super.toString()+" condition='"+condition+"'";
040 }
041
042 /**
043 * Evaluate condition as soon as all condition arguments are available.
044 */
045 @SuppressWarnings("unchecked")
046 @Override
047 protected boolean partialJoin(List<com.hammurapi.reasoning.impl.InferenceToken<F>>[] inputs, int index) throws Exception {
048 if (index==lastConditionPin) {
049 Object[] cArgs = new Object[conditionArguments.size()+1];
050 Iterator<ConditionArgument> cit = conditionArguments.iterator();
051 Z: for (int i=0; cit.hasNext(); ++i) {
052 ConditionArgument ca = cit.next();
053 for (int j=0; j<=index; ++j) {
054 List<TokenInfo> pinConfig = (List<TokenInfo>) getInputPins().get(j).config;
055 if (pinConfig.size()!=inputs[j].size()) {
056 throw new IllegalArgumentException("Number of input tokens is different from number of token infos");
057 }
058 for (int k=0, l=pinConfig.size(); k<l; ++k) {
059 TokenInfo ti = pinConfig.get(k);
060 if (ti.getParameterIndex() == ca.getParameterIndex()) {
061 InferenceToken<F> it = inputs[j].get(k);
062 if (it.isConsumed()) {
063 return false;
064 }
065
066 cArgs[i] = knowledgeBase.get(it.getHandle());
067 if (cArgs[i]==null) {
068 return false;
069 }
070 continue Z;
071 }
072 }
073 }
074 }
075
076 cArgs[cArgs.length-1] = rule;
077 try {
078 boolean result = Boolean.TRUE.equals(evaluator.evaluate(cArgs));
079 if (isFine) {
080 StringBuilder sb = new StringBuilder("Condition '"+condition+"' was evaluated to "+result+" for arguments ");
081 for (Object o: cArgs) {
082 sb.append(Constants.LINE_SEPARATOR+"\t"+o);
083 }
084 logger.fine(sb.toString());
085 }
086
087 return result;
088 } catch (Exception e) {
089 ExceptionHandler exceptionHandler = knowledgeBase.getExceptionHandler();
090 if (exceptionHandler==null) {
091 throw e;
092 }
093 exceptionHandler.handleException(e);
094 return false;
095 }
096 }
097
098 return true;
099 }
100
101 private boolean hasConditionArgument(PinEntry<F> pin) {
102 for (Object o: pin.config) {
103 TokenInfo ti = (TokenInfo) o;
104 for (ConditionArgument ca: conditionArguments) {
105 if (ca.getParameterIndex()==ti.getParameterIndex()) {
106 return true;
107 }
108 }
109 }
110
111 return false;
112 }
113
114 private int lastConditionPin=-1;
115
116 /**
117 * Sort input pins to move pins with conditon arguments to first positions.
118 */
119 @Override
120 protected void sortInputs() {
121 Collections.sort(getInputPins(), new Comparator<PinEntry<F>>() {
122
123 @Override
124 public int compare(PinEntry<F> pe1, PinEntry<F> pe2) {
125 if (hasConditionArgument(pe1)) {
126 if (hasConditionArgument(pe2)) {
127 return pe1.index-pe2.index;
128 }
129 return -1; // pe1 shall be before pe2
130 }
131
132 if (hasConditionArgument(pe2)) {
133 return 1; // pe1 shall be after pe2.
134 }
135
136 return pe1.index - pe2.index;
137 }
138 });
139
140 for (int i=0; i<getInputPins().size(); ++i) {
141 if (hasConditionArgument(getInputPins().get(i))) {
142 lastConditionPin = i;
143 }
144 }
145 }
146
147 @Override
148 public void afterConnect() throws ConfigurationException {
149 super.afterConnect();
150 try {
151 String[] pNames = new String[conditionArguments.size()+1];
152 Class<?>[] pTypes = new Class[pNames.length];
153 for (int i=0; i<conditionArguments.size(); ++i) {
154 ConditionArgument ca = conditionArguments.get(i);
155 pNames[i] = ca.getName();
156 pTypes[i] = ca.getType();
157 }
158
159 pNames[conditionArguments.size()] = "rule";
160 pTypes[conditionArguments.size()] = rule.getClass();
161
162 evaluator = new ExpressionEvaluator(condition, boolean.class, pNames, pTypes);
163 } catch (CompileException e) {
164 throw new ConfigurationException("Problem with condition '"+condition+"': "+e, e);
165 } catch (ParseException e) {
166 throw new ConfigurationException("Problem with condition '"+condition+"': "+e, e);
167 } catch (ScanException e) {
168 throw new ConfigurationException("Problem with condition '"+condition+"': "+e, e);
169 }
170 }
171
172 private ExpressionEvaluator evaluator;
173
174 }