View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.rule;
17  
18  import java.text.ParseException;
19  
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.log4j.Logger;
22  import org.kuali.rice.kew.engine.RouteContext;
23  import org.kuali.rice.kew.exception.WorkflowException;
24  import org.kuali.rice.kew.service.KEWServiceLocator;
25  
26  
27  /**
28   * Implements the KRA meta-rule processing and state machine engine 
29   * 
30   * @author Kuali Rice Team (rice.collab@kuali.org)
31   */
32  public class KRAMetaRuleEngine {
33      private static final Logger LOG = Logger.getLogger(KRAMetaRuleEngine.class);
34  
35      /**
36       * KRA meta-rule processing flag
37       */
38      private static enum KRA_RULE_FLAG {
39          NEXT, TRUE, FALSE
40      }
41  
42      private final String expression;
43      private final String[] statements;
44      private int curStatement = 0;
45      /**
46       * Whether processing has indicated that we should stop
47       */
48      private boolean stop = false;
49      
50      public KRAMetaRuleEngine(String expression) throws ParseException {
51          this.expression = expression;
52          statements = expression.split("\\s*[;\r\n]\\s*");
53          
54          if (statements.length == 0) {
55              throw new ParseException("No statements parsed in expression: " + expression, 0);
56          }
57      }
58  
59      /**
60       * @return the expression the engine was initialized with
61       */
62      public String getExpression() {
63          return expression;
64      }
65  
66      /**
67       * @return the parsed statements
68       */
69      public String[] getStatements() {
70          return statements;
71      }
72  
73      /**
74       * @return the next statement the engine will process
75       */
76      public int getCurStatement() {
77          return curStatement;
78      }
79  
80      /**
81       * @param statementNo the statement index at which to resume processing 
82       */
83      public void setCurStatement(int statementNo) {
84          this.curStatement = statementNo;
85      }
86  
87      /**
88       * @return whether we are done processing
89       */
90      public boolean isDone() {
91          return curStatement >= statements.length || stop;
92      }
93  
94      /**
95       * Processes a single statement and returns the result
96       * @param context the current RouteContext
97       * @return the expression result that resulted from the evaluation of a single statement
98       * @throws ParseException if the statement could not be parsed
99       */
100     public RuleExpressionResult processSingleStatement(RouteContext context) throws ParseException, WorkflowException {
101         if (isDone()) {
102             return null;
103         }
104 
105         int stmtNum = curStatement + 1;
106         String statement = statements[curStatement];
107         LOG.debug("Processing statement: " + statement);
108         String[] words = statement.split("\\s*:\\s*");
109         if (words.length < 2) {
110             throw new ParseException("Invalid statement (#" + stmtNum + "): " + statement, 0);
111         }
112         String ruleName = words[0];
113         if (StringUtils.isEmpty(ruleName)) {
114             throw new ParseException("Invalid rule in statement (#" + stmtNum + "): " + statement, 0);
115         }
116         String flag = words[1];
117         LOG.debug(flag.toUpperCase());
118         KRA_RULE_FLAG flagCode = KRA_RULE_FLAG.valueOf(flag.toUpperCase());
119         if (flagCode == null) {
120             throw new ParseException("Invalid flag in statement (#" + stmtNum + "): " + statement, 0);
121         }
122         RuleBaseValues nestedRule = KEWServiceLocator.getRuleService().getRuleByName(ruleName);
123         if (nestedRule == null) {
124             throw new ParseException("Rule '" + ruleName + "' in statement (#" + stmtNum + ") not found: " + statement, 0);
125         }
126 
127         Rule rule = new RuleImpl(nestedRule);
128         RuleExpressionResult result;
129         switch (flagCode) {
130             case NEXT:
131                 result = rule.evaluate(rule, context);
132                 break;
133             case TRUE:
134                 result = rule.evaluate(rule, context);
135                 if (!result.isSuccess()) {
136                     stop = true;
137                 }
138                 break;
139             case FALSE:
140                 result = rule.evaluate(rule, context);
141                 if (result.isSuccess()) {
142                     stop = true;
143                     // we need to just invert the ultimate expression result success, because in this case
144                     // we wanted the expression to fail but it didn't
145                     result = new RuleExpressionResult(rule, false, result.getResponsibilities());
146                 }
147                 break;
148             default:
149                 throw new WorkflowException("Unhandled statement flag: " + flagCode);
150         }
151 
152         curStatement++;
153         LOG.debug("Result of statement '" + statement + "': " + result);
154         return result;
155     }
156 }