    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    }