六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 19|回复: 0

利用Antlr开发状态机

[复制链接]

升级  96%

10

主题

10

主题

10

主题

童生

Rank: 1

积分
48
 楼主| 发表于 2013-2-3 10:38:59 | 显示全部楼层 |阅读模式
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();}}@Overridepublic Set<TState> getStates() {return dict.keySet();}@Overridepublic Set<TAction> getActions() {    Set<TAction> actionsSet = new HashSet<TAction>();    for (Map<TAction, TState> map : dict.values()) {    actionsSet.addAll(map.keySet());}            return actionsSet;}@Overridepublic Set<TAction> getValidActions(TState state) {if(!dict.containsKey(state)){throw new  RuntimeException("State not in the system");}return dict.get(state).keySet();}@Overridepublic 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();
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表