001    /*
002     * Copyright 2007-2008 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.kew.rule;
017    
018    import java.text.ParseException;
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.apache.log4j.Logger;
024    import org.kuali.rice.kew.engine.RouteContext;
025    import org.kuali.rice.kew.exception.WorkflowException;
026    
027    
028    /**
029     * Expression implementation for "meta rules".  A "meta rule" consists of a sequence of
030     * subordinate rule evaluations each processed according to an associated modifier:
031     * <dl>
032     *   <dt>next<dt>
033     *   <dd>proceed with rule evaluation</dd>
034     *   <dt>true</dt>
035     *   <dd>if this rule evaluates to true, then return the responsibilities associated with the rule</dd>
036     *   <dt>false</dt>
037     *   <dd>if this rule evaluates to false, then return the responsibilities associated with the rule</dd>
038     * </dl>
039     * E.g.
040     * <div><tt>bizRule1: next; bizRule2: true; bizRule3: false</tt></div>
041     * @author Kuali Rice Team (rice.collab@kuali.org)
042     */
043    public class MetaRuleExpression extends AccumulatingBSFRuleExpression {
044        private static final Logger LOG = Logger.getLogger(MetaRuleExpression.class);
045    
046        @Override
047        public RuleExpressionResult evaluate(Rule rule, RouteContext context) throws WorkflowException {
048            RuleBaseValues ruleDefinition = rule.getDefinition();
049            RuleExpressionDef exprDef = ruleDefinition.getRuleExpressionDef();
050            if (exprDef == null) {
051                throw new WorkflowException("No expression defined in rule definition: " + ruleDefinition);
052            }
053            String expression = exprDef.getExpression();
054            if (StringUtils.isEmpty(expression)) {
055                throw new WorkflowException("Empty expression in rule definition: " + ruleDefinition);
056            }
057    
058            String lang = parseLang(ruleDefinition.getRuleExpressionDef().getType(), null);
059            if (lang == null) {
060                // if no language qualifier is specified, parse it as a built-in meta rule expression
061                return evaluateBuiltinExpression(expression, rule, context);
062            } else {
063                return super.evaluate(rule, context);
064            }
065        }
066    
067        /**
068         * Evaluates the builtin "meta" rule expression
069         * @param expression the builtin meta rule expression
070         * @param rule the rule
071         * @param context the route context
072         * @return RuleExpressionResult the result
073         * @throws WorkflowException
074         */
075        private RuleExpressionResult evaluateBuiltinExpression(String expression, Rule rule, RouteContext context) throws WorkflowException {
076            try {
077                KRAMetaRuleEngine engine = new KRAMetaRuleEngine(expression);
078    
079                int responsibilityPriority = 0; // responsibility priority, lower value means higher priority (due to sort)...increment as we go
080                RuleExpressionResult result = null;
081                boolean success = false;
082                List<RuleResponsibility> responsibilities = new ArrayList<RuleResponsibility>();
083                while (!engine.isDone()) {
084                    result = engine.processSingleStatement(context);
085                    if (result.isSuccess() && result.getResponsibilities() != null) {
086                        // accumulate responsibilities if the evaluation was successful
087                        // make sure to reduce priority for each subsequent rule in order for sequential activation to work as desired
088                        for (RuleResponsibility responsibility: result.getResponsibilities()) {
089                            responsibility.setPriority(Integer.valueOf(responsibilityPriority));
090                            responsibilities.add(responsibility);
091                        }
092                        // decrement responsibilityPriority for next rule expression result responsibilities
093                        responsibilityPriority++;
094                        success = true;
095                    }
096                }
097                result = new RuleExpressionResult(rule, success, responsibilities);
098                LOG.debug("KRAMetaRuleExpression returning result: " + result);
099                return result;
100            } catch (ParseException pe) {
101                throw new WorkflowException("Error parsing expression", pe);
102            }
103        }
104    }