001 /*
002 * Copyright 2007 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
020 import org.apache.commons.lang.StringUtils;
021 import org.apache.log4j.Logger;
022 import org.kuali.rice.core.api.exception.RiceIllegalStateException;
023 import org.kuali.rice.kew.engine.RouteContext;
024 import org.kuali.rice.kew.exception.WorkflowException;
025 import org.kuali.rice.kew.service.KEWServiceLocator;
026
027
028 /**
029 * Implements the KRA meta-rule processing and state machine engine
030 *
031 * @author Kuali Rice Team (rice.collab@kuali.org)
032 */
033 public class KRAMetaRuleEngine {
034 private static final Logger LOG = Logger.getLogger(KRAMetaRuleEngine.class);
035
036 /**
037 * KRA meta-rule processing flag
038 */
039 private static enum KRA_RULE_FLAG {
040 NEXT, TRUE, FALSE
041 }
042
043 private final String expression;
044 private final String[] statements;
045 private int curStatement = 0;
046 /**
047 * Whether processing has indicated that we should stop
048 */
049 private boolean stop = false;
050
051 public KRAMetaRuleEngine(String expression) throws ParseException {
052 this.expression = expression;
053 statements = expression.split("\\s*[;\r\n]\\s*");
054
055 if (statements.length == 0) {
056 throw new ParseException("No statements parsed in expression: " + expression, 0);
057 }
058 }
059
060 /**
061 * @return the expression the engine was initialized with
062 */
063 public String getExpression() {
064 return expression;
065 }
066
067 /**
068 * @return the parsed statements
069 */
070 public String[] getStatements() {
071 return statements;
072 }
073
074 /**
075 * @return the next statement the engine will process
076 */
077 public int getCurStatement() {
078 return curStatement;
079 }
080
081 /**
082 * @param statementNo the statement index at which to resume processing
083 */
084 public void setCurStatement(int statementNo) {
085 this.curStatement = statementNo;
086 }
087
088 /**
089 * @return whether we are done processing
090 */
091 public boolean isDone() {
092 return curStatement >= statements.length || stop;
093 }
094
095 /**
096 * Processes a single statement and returns the result
097 * @param context the current RouteContext
098 * @return the expression result that resulted from the evaluation of a single statement
099 * @throws ParseException if the statement could not be parsed
100 */
101 public RuleExpressionResult processSingleStatement(RouteContext context) throws ParseException {
102 if (isDone()) {
103 return null;
104 }
105
106 int stmtNum = curStatement + 1;
107 String statement = statements[curStatement];
108 LOG.debug("Processing statement: " + statement);
109 String[] words = statement.split("\\s*:\\s*");
110 if (words.length < 2) {
111 throw new ParseException("Invalid statement (#" + stmtNum + "): " + statement, 0);
112 }
113 String ruleName = words[0];
114 if (StringUtils.isEmpty(ruleName)) {
115 throw new ParseException("Invalid rule in statement (#" + stmtNum + "): " + statement, 0);
116 }
117 String flag = words[1];
118 LOG.debug(flag.toUpperCase());
119 KRA_RULE_FLAG flagCode = KRA_RULE_FLAG.valueOf(flag.toUpperCase());
120 if (flagCode == null) {
121 throw new ParseException("Invalid flag in statement (#" + stmtNum + "): " + statement, 0);
122 }
123 RuleBaseValues nestedRule = KEWServiceLocator.getRuleService().getRuleByName(ruleName);
124 if (nestedRule == null) {
125 throw new ParseException("Rule '" + ruleName + "' in statement (#" + stmtNum + ") not found: " + statement, 0);
126 }
127
128 Rule rule = new RuleImpl(nestedRule);
129 RuleExpressionResult result;
130 switch (flagCode) {
131 case NEXT:
132 result = rule.evaluate(rule, context);
133 break;
134 case TRUE:
135 result = rule.evaluate(rule, context);
136 if (!result.isSuccess()) {
137 stop = true;
138 }
139 break;
140 case FALSE:
141 result = rule.evaluate(rule, context);
142 if (result.isSuccess()) {
143 stop = true;
144 // we need to just invert the ultimate expression result success, because in this case
145 // we wanted the expression to fail but it didn't
146 result = new RuleExpressionResult(rule, false, result.getResponsibilities());
147 }
148 break;
149 default:
150 throw new RiceIllegalStateException("Unhandled statement flag: " + flagCode);
151 }
152
153 curStatement++;
154 LOG.debug("Result of statement '" + statement + "': " + result);
155 return result;
156 }
157 }