利用Antlr开发状态机

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

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

grammarStateMachine;

options{

output=AST;

ASTLabelType=CommonTree;

}

tokens{

RULE_ROOT;

STATE_DECLARATION;

CASE_CLAUSE;

CASE_DECLARATION;

}

@header{packagecompiler.statemachine;}

@lexer::header{packagecompiler.statemachine;}

ruleRoot

:

stateDeclaration*EOF

->^(RULE_ROOTstateDeclaration*)

;

stateDeclaration

:

Identifier'{'caseDeclaration*'}'';'?

->^(STATE_DECLARATIONIdentifier^(CASE_CLAUSEcaseDeclaration*))

;

caseDeclaration

:

Identifier'=>'Identifier';'

->^(CASE_DECLARATIONIdentifier+)

;

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=newHashMap<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);

}

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

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

实际运用中结合Spring可以优化

StateMachineImpl<RequestState,RequestAction>stateMachine=newStateMachineImpl<RequestState,RequestAction>();

stateMachine.setStateType(RequestState.class);

stateMachine.setActionType(RequestAction.class);

stateMachine.complieRule();

相关推荐