利用Antlr开发状态机

Antlr 不用多介绍了,只想说此乃神器也~~~

进入正题,首先是Antlr 定义的语法:

grammar StateMachine;

options {
output=AST;
ASTLabelType=CommonTree;
}

tokens {
RULE_ROOT;
STATE_DECLARATION;
CASE_CLAUSE;
CASE_DECLARATION;
}

@header {package compiler.statemachine;}
@lexer::header {package compiler.statemachine;}



ruleRoot
:
stateDeclaration* EOF
->^(RULE_ROOT stateDeclaration*)
;

stateDeclaration
:
Identifier '{' caseDeclaration* '}' ';'?
->^(STATE_DECLARATION Identifier ^(CASE_CLAUSE caseDeclaration*))
;

caseDeclaration
:
Identifier '=>' Identifier ';'
->^(CASE_DECLARATION Identifier+)
;

Identifier
:
('A'..'Z'|'a'..'z'|'_')('A'..'Z'|'a'..'z'|'0'..'9'|'_')*
;

COMMENT
:
'//' ~('\n'|'\r')* '\r'? ('\n'|EOF) {$channel=HIDDEN;}
    |
    '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

WS
:
(' '|'\t'|'\r'|'\u000C'|'\n') {$channel=HIDDEN;}
;



从语法定义中可以看出,我们使用时候需要输入的格式为

状态{动作=>新状态}


比如我们有业务是,商务专员填写好报价单后,提交到招标经理,招标经理审批通过后,提交到大区经理。

现在来定义我们业务中所会用到的State 和Action 的枚举
package compiler.statemachine;
public enum RequestState {
	 UnInitialized,
	 CommercialApplying, // 商务申请报价单
	 BiddingManagerAuditing, //招标经理审批报价单
	 CDManagerAuditing //大区经理审批报价单
}

package compiler.statemachine;
public enum RequestAction {
	 CommercialCreate,  //商务专员创建报价单
	 CommercialModify,  //商务专员修改报价单
	 CommercialCommit,  //商务专员提交报价单
	 BiddingManagerModify, //招标经理修改报价单
	 BiddingManagerApprove //招标经理审批通过报价单
}



两个枚举根据实际业务可以自由修改,比如 招标经理拒绝报价单等


接下来是重头戏,如何解析由Antlr生成的抽象语法树!

先定义StateMachine 接口

package compiler.statemachine;

import java.util.Set;

public interface StateMachine<TState, TAction> {
	Set<TState> getStates() ;
	
	Set<TAction> getActions() ;
	
	Set<TAction> getValidActions(TState state);
	
	TState changeState(TState currentState, TAction action);
	
}


然后编写实现类

package compiler.statemachine;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.Tree;

public class StateMachineImpl<TState extends Enum<TState>, TAction extends Enum<TAction>> implements StateMachine<TState, TAction>{

	private Class<TState> stateType;

    private Class<TAction> actionType;
    
	private String rule;
	
	private Map<TState,Map<TAction,TState>> dict = new HashMap<TState,Map<TAction,TState>>();
	
	public Class<TState> getStateType() {
		return stateType;
	}

	public void setStateType(Class<TState> stateType) {
		this.stateType = stateType;
	}

	public Class<TAction> getActionType() {
		return actionType;
	}

	public void setActionType(Class<TAction> actionType) {
		this.actionType = actionType;
	}

	public String getRule() {
		return rule;
	}

	public void setRule(String rule) {
		this.rule = rule;
	}
	
	public StateMachineImpl(){
		
	}
	
	
	public StateMachineImpl(Class<TState> state,Class<TAction> action){
		this.stateType = state;
		this.actionType = action;
	}

	public void complieRule(){

		StateMachineLexer lexer = new StateMachineLexer(new ANTLRStringStream(rule));
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		StateMachineParser parser = new StateMachineParser(tokens);
		try {
			Tree ruleRootNode = (Tree)parser.ruleRoot().getTree();
			for(int i=0;i<ruleRootNode.getChildCount();i++){
				
				Tree  stateDeclarationNode = ruleRootNode.getChild(i);
				String stateText =  stateDeclarationNode.getChild(0).getText();			
				TState state;
				state = (TState) Enum.valueOf(this.stateType, stateText);	
				System.out.println(state.getClass());
				Map<TAction,TState> nestedDict = new HashMap<TAction,TState>();				
				dict.put(state, nestedDict);
				
				for(int ii=0;ii<stateDeclarationNode.getChildCount();ii++){
					Tree caseClauseNode = stateDeclarationNode.getChild(ii);
					for(int iii=0;iii<caseClauseNode.getChildCount();iii++){
						 Tree caseDeclarationNode = caseClauseNode.getChild(iii);
						 String actionText = caseDeclarationNode.getChild(0).getText();
						 String targetStateText = caseDeclarationNode.getChild(1).getText();
						 TAction action;
	                     TState targetState;
	                     
	                     action = (TAction) Enum.valueOf(this.actionType, actionText);
	                     targetState = (TState) Enum.valueOf(this.stateType, targetStateText);
	                     
	                     nestedDict.put(action, targetState);
					}
					
				}
				
			}
			
		} catch (RecognitionException e) {
			e.printStackTrace();
		}
		
		
	}
	
	@Override
	public Set<TState> getStates() {
		
		return dict.keySet();
	}

	@Override
	public Set<TAction> getActions() {
	    Set<TAction> actionsSet = new HashSet<TAction>();
	    for (Map<TAction, TState> map : dict.values()) {
	    	actionsSet.addAll(map.keySet());
		}	    	  
	  
	    return actionsSet;
	}

	@Override
	public Set<TAction> getValidActions(TState state) {
		if(!dict.containsKey(state)){
			throw new  RuntimeException("State not in the system");
		}
		return dict.get(state).keySet();
	}

	@Override
	public TState changeState(TState currentState, TAction action) {
		if(!dict.containsKey(currentState)){
			throw new IllegalArgumentException();
		}
		
		Map<TAction,TState> rules = dict.get(currentState);
		
		TState returnState = rules.get(action);
	
		if(returnState==null){
			throw new UnsupportedOperationException();
		}
			
		return returnState;
	}


}



最主要的就是complieRule 方法
解析Antlr 生成的抽象语法树,把 状态{动作=>新状态}这样格式的字符串,转换为
Map<TState,Map<TAction,TState>> dict = new HashMap<TState,Map<TAction,TState>>()
这样的一个Map

最后编写测试类

package test.statemachine;
import java.util.Map;
import java.util.Set;

import compiler.statemachine.EnumerationStateMechineLocalObject;
import compiler.statemachine.RequestAction;
import compiler.statemachine.RequestState;
import compiler.statemachine.StateMachineImpl;

public class StateMachineTest {

	public static void main(String[] args) {
		StateMachineTest();
	}

	
	public static void StateMachineTest() {
		StateMachineImpl<RequestState, RequestAction> stateMachine = new StateMachineImpl<RequestState, RequestAction>();				
		stateMachine.setRule("UnInitialized { CommercialCreate => CommercialApplying;}  CommercialApplying {CommercialModify => CommercialApplying;CommercialCommit => BiddingManagerAuditing;BiddingManagerModify => CommercialApplying; BiddingManagerApprove => CDManagerAuditing; }");
		stateMachine.setStateType(RequestState.class);
		stateMachine.setActionType(RequestAction.class);
		
		stateMachine.complieRule();
		RequestState requestState = RequestState.CommercialApplying;
		
		Set<RequestAction> currentActions = stateMachine.getValidActions(requestState);
		
		if(currentActions.contains(RequestAction.BiddingManagerApprove)){
			requestState = stateMachine.changeState(requestState, RequestAction.BiddingManagerApprove);
		}
		
		Set<RequestAction> actions = stateMachine.getActions();
		
		for (RequestAction requestAction : actions) {
			System.out.println(requestAction);
		}
		System.out.println(requestState);
	}

}




从代码:
stateMachine.setRule("UnInitialized { CommercialCreate => CommercialApplying;}  CommercialApplying {CommercialModify => CommercialApplying;CommercialCommit => BiddingManagerAuditing;BiddingManagerModify => CommercialApplying; BiddingManagerApprove => CDManagerAuditing; }");

可以看出 输入字符串
"UnInitialized { CommercialCreate => CommercialApplying;}  CommercialApplying {CommercialModify => CommercialApplying;CommercialCommit => BiddingManagerAuditing;BiddingManagerModify => CommercialApplying; BiddingManagerApprove => CDManagerAuditing; }"

调用stateMachine.complieRule();

通过 stateMachine.getValidActions 得到 当前状态的报价单所对应的所有可以执行的Action

例如现在招标经理要将商务提交的报价单审批通过;

则通过Set<RequestAction> currentActions = stateMachine.getValidActions(requestState);

得到所有能够执行的Action

通过if(currentActions.contains(RequestAction.BiddingManagerApprove)){
requestState = stateMachine.changeState(requestState, RequestAction.BiddingManagerApprove);
}

来改变报价单的状态从 RequestState requestState = RequestState.CommercialApplying;

报价单状态从,商务申请中变为,大区经理审批中 CDManagerAuditing

实际运用中结合Spring可以优化
StateMachineImpl<RequestState, RequestAction> stateMachine = new StateMachineImpl<RequestState, RequestAction>();

stateMachine.setStateType(RequestState.class);
stateMachine.setActionType(RequestAction.class);
stateMachine.complieRule();



猜你喜欢

转载自sortaxie.iteye.com/blog/1545699
今日推荐