View Javadoc

1   /**
2    * Copyright 2005-2013 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.core.api.exception.RiceIllegalStateException;
23  import org.kuali.rice.kew.api.KewApiServiceLocator;
24  import org.kuali.rice.kew.engine.RouteContext;
25  
26  /**
27   * Implements the KRA meta-rule processing and state machine engine 
28   * 
29   * @author Kuali Rice Team (rice.collab@kuali.org)
30   */
31  public class KRAMetaRuleEngine {
32      private static final Logger LOG = Logger.getLogger(KRAMetaRuleEngine.class);
33  
34      /**
35       * KRA meta-rule processing flag
36       */
37      private static enum KRA_RULE_FLAG {
38          NEXT, TRUE, FALSE
39      }
40  
41      private final String expression;
42      private final String[] statements;
43      private int curStatement = 0;
44      /**
45       * Whether processing has indicated that we should stop
46       */
47      private boolean stop = false;
48      
49      public KRAMetaRuleEngine(String expression) throws ParseException {
50          this.expression = expression;
51          statements = expression.split("\\s*[;\r\n]\\s*");
52          
53          if (statements.length == 0) {
54              throw new ParseException("No statements parsed in expression: " + expression, 0);
55          }
56      }
57  
58      /**
59       * @return the expression the engine was initialized with
60       */
61      public String getExpression() {
62          return expression;
63      }
64  
65      /**
66       * @return the parsed statements
67       */
68      public String[] getStatements() {
69          return statements;
70      }
71  
72      /**
73       * @return the next statement the engine will process
74       */
75      public int getCurStatement() {
76          return curStatement;
77      }
78  
79      /**
80       * @param statementNo the statement index at which to resume processing 
81       */
82      public void setCurStatement(int statementNo) {
83          this.curStatement = statementNo;
84      }
85  
86      /**
87       * @return whether we are done processing
88       */
89      public boolean isDone() {
90          return curStatement >= statements.length || stop;
91      }
92  
93      /**
94       * Processes a single statement and returns the result
95       * @param context the current RouteContext
96       * @return the expression result that resulted from the evaluation of a single statement
97       * @throws ParseException if the statement could not be parsed
98       */
99      public RuleExpressionResult processSingleStatement(RouteContext context) throws ParseException {
100         if (isDone()) {
101             return null;
102         }
103 
104         int stmtNum = curStatement + 1;
105         String statement = statements[curStatement];
106         LOG.debug("Processing statement: " + statement);
107         String[] words = statement.split("\\s*:\\s*");
108         if (words.length < 2) {
109             throw new ParseException("Invalid statement (#" + stmtNum + "): " + statement, 0);
110         }
111         String ruleName = words[0];
112         if (StringUtils.isEmpty(ruleName)) {
113             throw new ParseException("Invalid rule in statement (#" + stmtNum + "): " + statement, 0);
114         }
115         String flag = words[1];
116         LOG.debug(flag.toUpperCase());
117         KRA_RULE_FLAG flagCode = KRA_RULE_FLAG.valueOf(flag.toUpperCase());
118         if (flagCode == null) {
119             throw new ParseException("Invalid flag in statement (#" + stmtNum + "): " + statement, 0);
120         }
121         org.kuali.rice.kew.api.rule.Rule nestedRule = KewApiServiceLocator.getRuleService().getRuleByName(ruleName);
122         if (nestedRule == null) {
123             throw new ParseException("Rule '" + ruleName + "' in statement (#" + stmtNum + ") not found: " + statement, 0);
124         }
125 
126         Rule rule = new RuleImpl(nestedRule);
127         RuleExpressionResult result;
128         switch (flagCode) {
129             case NEXT:
130                 result = rule.evaluate(rule, context);
131                 break;
132             case TRUE:
133                 result = rule.evaluate(rule, context);
134                 if (!result.isSuccess()) {
135                     stop = true;
136                 }
137                 break;
138             case FALSE:
139                 result = rule.evaluate(rule, context);
140                 if (result.isSuccess()) {
141                     stop = true;
142                     // we need to just invert the ultimate expression result success, because in this case
143                     // we wanted the expression to fail but it didn't
144                     result = new RuleExpressionResult(rule, false, result.getResponsibilities());
145                 }
146                 break;
147             default:
148                 throw new RiceIllegalStateException("Unhandled statement flag: " + flagCode);
149         }
150 
151         curStatement++;
152         LOG.debug("Result of statement '" + statement + "': " + result);
153         return result;
154     }
155 }