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    }