利用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();