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 }