最早接触xml配置文件驱动事件是在实习的时候,那时候恶毒的项目经理让我研究一个工作流引擎,鼎鼎大名的OSWORKFLOW,那东西是JAVA写的,唉~~太强人所难了....最后当然是可耻的失败鸟。
最近在研究业务架构的时候,偶尔发现了一个XML引擎驱动事件的程序,fm2.0的,倒不是为了报仇,只是我一直没弄明白到底xml配置是怎么驱动事件的。就看了看,多少有点收获,跟大家分享一下
工欲善其事,必先利其器,所以首先,要有个好的XML读写方法集,我看了看好多大家可能都用得上,就放在这了, 当个参考资料
Instance Data members#region Instance Data members
/**//// <summary>
/// File name to read from
/// </summary>
string m_sFilename = "";
/**//// <summary>
/// Rules engine to populate
/// </summary>
RulesEngine m_oRulesEngine;
/**//// <summary>
/// Namespace manager to use in XPath
/// </summary>
XmlNamespaceManager m_oNSMgr;
/**//// <summary>
/// Xml DOM document to use in reading
/// </summary>
XmlDocument m_oXmlDoc;
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public JaxlabReader()
{
m_oRulesEngine = new RulesEngine();
}
/**//// <summary>
/// Constructor that also specifies the XML file name.
/// </summary>
/// <remarks>
/// This constructor calls the LoadEngineByFileName method.
/// </remarks>
/// <param name="sFilename">the XML file location (including UNC or HTTP)</param>
public JaxlabReader(string sFilename)
{
m_oRulesEngine = new RulesEngine();
LoadEngineByFileName(sFilename);
}
#endregion
/**//// <summary>
/// Loads the
/// </summary>
/// <param name="sFilename">the XML file location (including UNC or HTTP)</param>
public void LoadEngineByFileName(string sFilename)
{
try
{
m_sFilename = sFilename;
XmlDocument oXmlDoc = new XmlDocument();
oXmlDoc.Load(m_sFilename);
LoadEngineByXmlDoc(oXmlDoc);
}
catch(Exception err)
{
System.Console.WriteLine(err.Message + "\n" + err.StackTrace);
}
}
/**//// <summary>
/// Loads the Rules Engine by in-memory XmlDocument.
/// </summary>
/// <param name="oXmlDoc">the in-memory XmlDocument</param>
public void LoadEngineByXmlDoc(XmlDocument oXmlDoc)
{
try
{
m_oXmlDoc = oXmlDoc;
m_oNSMgr = new XmlNamespaceManager(m_oXmlDoc.NameTable);
LoadXmlVariables(m_oXmlDoc.DocumentElement);
XmlNodeList oRuleNodeList = m_oXmlDoc.SelectNodes(".//Rules/Rule", m_oNSMgr);
for(int i = 0; i < oRuleNodeList.Count; i++)
{
XmlNode oXmlRule = oRuleNodeList[i];
LoadXMLRule(null, oXmlRule);
}
XmlNode oTriggersNode = m_oXmlDoc.SelectSingleNode("//Triggers", m_oNSMgr);
if(oTriggersNode != null)
{
LoadTrigger(oTriggersNode);
}
}
catch(Exception err)
{
System.Console.WriteLine(err.Message + "\n" + err.StackTrace);
}
}
/**//// <summary>
/// Loads an XML Node into the Rules Engine.
/// </summary>
/// <param name="oXmlRule">XmlNode of a RulesObject</param>
public void LoadXmlRule(XmlNode oXmlRule)
{
LoadXMLRule(null, oXmlRule);
}
private void LoadXmlVariables(XmlNode oXmlRoot)
{
XmlNodeList oXmlVars = oXmlRoot.SelectNodes(".//Variable");
for(int i = 0; i < oXmlVars.Count; i++)
{
XmlNode oXmlVar = oXmlVars[i];
string sVarID = oXmlVar.Attributes.GetNamedItem("id").Value;
string sValue = oXmlVar.InnerText;
Variable oVar = new Variable(sVarID, sValue);
m_oRulesEngine.AddVariable(oVar);
}
}
private void LoadXMLRule(RuleObject oParentRule, XmlNode oXmlRule)
{
string sID = oXmlRule.Attributes.GetNamedItem("id").Value;
XmlNode xmlAttrUse = oXmlRule.Attributes.GetNamedItem("useInferencing");
XmlNode oExpr = oXmlRule.SelectSingleNode("Expression", m_oNSMgr);
if(sID != "" && oExpr != null)
{
RuleObject oRule = new RuleObject(sID, oExpr.InnerText);
if(xmlAttrUse != null)
{
string sUse = xmlAttrUse.Value;
string sUseAdj = sUse.Trim().ToLower();
if(sUseAdj.CompareTo("true") == 0)
{
oRule.UseInferencing = true;
}
else
{
oRule.UseInferencing = false;
}
}
XmlNode oXmlActions = oXmlRule.SelectSingleNode("Actions", m_oNSMgr);
XmlNodeList oXmlActionList = oXmlActions.SelectNodes("./Action", m_oNSMgr);
for(int a = 0; a < oXmlActionList.Count; a++)
{
XmlNode oXmlAction = oXmlActionList[a];
Action oAction = MakeAction(oXmlAction);
oRule.AddAction(oAction);
}
XmlNode oXmlSubRules = oXmlRule.SelectSingleNode("SubRules", m_oNSMgr);
XmlNodeList oXmlSubRuleList = oXmlSubRules.SelectNodes("./Rule", m_oNSMgr);
for(int s = 0; s < oXmlSubRuleList.Count; s++)
{
XmlNode oXmlSubRule = oXmlSubRuleList[s];
LoadXMLRule(oRule, oXmlSubRule);
}
if(oParentRule != null)
{
oParentRule.AddSubRule(oRule);
}
else
{
this.m_oRulesEngine.Add(oRule);
}
}
}
private Action MakeAction(XmlNode oXmlAction)
{
XmlNode oMatch = oXmlAction.SelectSingleNode("Match", m_oNSMgr);
XmlNode oAssign = oXmlAction.SelectSingleNode("Assign", m_oNSMgr);
string sMatch = "";
string sAssign = "";
if(oMatch != null)
{
sMatch = oMatch.InnerText;
}
string sAssignType = "none";
if(oAssign != null)
{
XmlNode oAssignType = oAssign.Attributes.GetNamedItem("type");
if(oAssignType != null)
{
sAssignType = oAssignType.Value;
}
sAssign = oAssign.InnerText;
}
string sActionID = oXmlAction.Attributes.GetNamedItem("actionId").InnerText;
string sType = oXmlAction.Attributes.GetNamedItem("type").InnerText;
Action.ActionType oType = ParseActionType(sType);
Action oAction = new Action(sAssignType, sMatch, sAssign, oType, sActionID);
if(sAssignType.CompareTo("object") == 0)
{
XmlNode oXmlNodeAssign = oAssign.FirstChild;
XMLNodeWrapper oWrapped = new XMLNodeWrapper(oXmlNodeAssign);
oAction = new Action(sAssignType, sMatch, oWrapped, oType, sActionID);
}
return oAction;
}
private Action.ActionType ParseActionType(string sType)
{
if(sType.ToLower().CompareTo("var") == 0)
{
return Action.ActionType.Var;
}
else if(sType.ToLower().CompareTo("makevar") == 0)
{
return Action.ActionType.MakeVar;
}
else if(sType.ToLower().CompareTo("run") == 0)
{
return Action.ActionType.Run;
}
else if(sType.ToLower().CompareTo("stop") == 0)
{
return Action.ActionType.Stop;
}
else
{
return Action.ActionType.None;
}
}
Trigger Loading#region Trigger Loading
private void LoadTrigger(XmlNode oXmlNode)
{
XmlNodeList oXTriggers = oXmlNode.ChildNodes;
for(int i = 0; i < oXTriggers.Count; i++)
{
XmlNode oXTrigger = oXTriggers[i];
XmlNode oIDAttr = oXTrigger.Attributes.GetNamedItem("id");
XmlNode oVarIDAttr = oXTrigger.Attributes.GetNamedItem("varID");
XmlNode oRuleIDAttr = oXTrigger.Attributes.GetNamedItem("ruleID");
string sID = GetAttrValue(oIDAttr);
string sVarID = GetAttrValue(oVarIDAttr);
string sRuleID = GetAttrValue(oRuleIDAttr);
Trigger oTrigger = new Trigger(sID, sVarID, sRuleID);
this.m_oRulesEngine.AddTrigger(oTrigger);
}
}
#endregion
private string GetAttrValue(XmlNode oAttrNode)
{
if(oAttrNode != null)
{
return oAttrNode.Value;
}
else
{
return "";
}
}
Properties#region Properties
/**//// <summary>
/// The RulesEngine object to use with your project.
/// </summary>
public RulesEngine NewRulesEngine
{
get
{
return this.m_oRulesEngine;
}
set
{
this.m_oRulesEngine = value;
}
}
#endregion
这是一段对应的XML配置文件片断
引擎中最重要的部分我认为是解析器,也可以说是简单的语法分析方法集,在面向对象的设计思路下,就要将他设计成一个类,也就是实现消化输入信息的功能。将在下一篇文章中具体介绍
最近在研究业务架构的时候,偶尔发现了一个XML引擎驱动事件的程序,fm2.0的,倒不是为了报仇,只是我一直没弄明白到底xml配置是怎么驱动事件的。就看了看,多少有点收获,跟大家分享一下
工欲善其事,必先利其器,所以首先,要有个好的XML读写方法集,我看了看好多大家可能都用得上,就放在这了, 当个参考资料
Instance Data members#region Instance Data members
/**//// <summary>
/// File name to read from
/// </summary>
string m_sFilename = "";
/**//// <summary>
/// Rules engine to populate
/// </summary>
RulesEngine m_oRulesEngine;
/**//// <summary>
/// Namespace manager to use in XPath
/// </summary>
XmlNamespaceManager m_oNSMgr;
/**//// <summary>
/// Xml DOM document to use in reading
/// </summary>
XmlDocument m_oXmlDoc;
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public JaxlabReader()
{
m_oRulesEngine = new RulesEngine();
}
/**//// <summary>
/// Constructor that also specifies the XML file name.
/// </summary>
/// <remarks>
/// This constructor calls the LoadEngineByFileName method.
/// </remarks>
/// <param name="sFilename">the XML file location (including UNC or HTTP)</param>
public JaxlabReader(string sFilename)
{
m_oRulesEngine = new RulesEngine();
LoadEngineByFileName(sFilename);
}
#endregion
/**//// <summary>
/// Loads the
/// </summary>
/// <param name="sFilename">the XML file location (including UNC or HTTP)</param>
public void LoadEngineByFileName(string sFilename)
{
try
{
m_sFilename = sFilename;
XmlDocument oXmlDoc = new XmlDocument();
oXmlDoc.Load(m_sFilename);
LoadEngineByXmlDoc(oXmlDoc);
}
catch(Exception err)
{
System.Console.WriteLine(err.Message + "\n" + err.StackTrace);
}
}
/**//// <summary>
/// Loads the Rules Engine by in-memory XmlDocument.
/// </summary>
/// <param name="oXmlDoc">the in-memory XmlDocument</param>
public void LoadEngineByXmlDoc(XmlDocument oXmlDoc)
{
try
{
m_oXmlDoc = oXmlDoc;
m_oNSMgr = new XmlNamespaceManager(m_oXmlDoc.NameTable);
LoadXmlVariables(m_oXmlDoc.DocumentElement);
XmlNodeList oRuleNodeList = m_oXmlDoc.SelectNodes(".//Rules/Rule", m_oNSMgr);
for(int i = 0; i < oRuleNodeList.Count; i++)
{
XmlNode oXmlRule = oRuleNodeList[i];
LoadXMLRule(null, oXmlRule);
}
XmlNode oTriggersNode = m_oXmlDoc.SelectSingleNode("//Triggers", m_oNSMgr);
if(oTriggersNode != null)
{
LoadTrigger(oTriggersNode);
}
}
catch(Exception err)
{
System.Console.WriteLine(err.Message + "\n" + err.StackTrace);
}
}
/**//// <summary>
/// Loads an XML Node into the Rules Engine.
/// </summary>
/// <param name="oXmlRule">XmlNode of a RulesObject</param>
public void LoadXmlRule(XmlNode oXmlRule)
{
LoadXMLRule(null, oXmlRule);
}
private void LoadXmlVariables(XmlNode oXmlRoot)
{
XmlNodeList oXmlVars = oXmlRoot.SelectNodes(".//Variable");
for(int i = 0; i < oXmlVars.Count; i++)
{
XmlNode oXmlVar = oXmlVars[i];
string sVarID = oXmlVar.Attributes.GetNamedItem("id").Value;
string sValue = oXmlVar.InnerText;
Variable oVar = new Variable(sVarID, sValue);
m_oRulesEngine.AddVariable(oVar);
}
}
private void LoadXMLRule(RuleObject oParentRule, XmlNode oXmlRule)
{
string sID = oXmlRule.Attributes.GetNamedItem("id").Value;
XmlNode xmlAttrUse = oXmlRule.Attributes.GetNamedItem("useInferencing");
XmlNode oExpr = oXmlRule.SelectSingleNode("Expression", m_oNSMgr);
if(sID != "" && oExpr != null)
{
RuleObject oRule = new RuleObject(sID, oExpr.InnerText);
if(xmlAttrUse != null)
{
string sUse = xmlAttrUse.Value;
string sUseAdj = sUse.Trim().ToLower();
if(sUseAdj.CompareTo("true") == 0)
{
oRule.UseInferencing = true;
}
else
{
oRule.UseInferencing = false;
}
}
XmlNode oXmlActions = oXmlRule.SelectSingleNode("Actions", m_oNSMgr);
XmlNodeList oXmlActionList = oXmlActions.SelectNodes("./Action", m_oNSMgr);
for(int a = 0; a < oXmlActionList.Count; a++)
{
XmlNode oXmlAction = oXmlActionList[a];
Action oAction = MakeAction(oXmlAction);
oRule.AddAction(oAction);
}
XmlNode oXmlSubRules = oXmlRule.SelectSingleNode("SubRules", m_oNSMgr);
XmlNodeList oXmlSubRuleList = oXmlSubRules.SelectNodes("./Rule", m_oNSMgr);
for(int s = 0; s < oXmlSubRuleList.Count; s++)
{
XmlNode oXmlSubRule = oXmlSubRuleList[s];
LoadXMLRule(oRule, oXmlSubRule);
}
if(oParentRule != null)
{
oParentRule.AddSubRule(oRule);
}
else
{
this.m_oRulesEngine.Add(oRule);
}
}
}
private Action MakeAction(XmlNode oXmlAction)
{
XmlNode oMatch = oXmlAction.SelectSingleNode("Match", m_oNSMgr);
XmlNode oAssign = oXmlAction.SelectSingleNode("Assign", m_oNSMgr);
string sMatch = "";
string sAssign = "";
if(oMatch != null)
{
sMatch = oMatch.InnerText;
}
string sAssignType = "none";
if(oAssign != null)
{
XmlNode oAssignType = oAssign.Attributes.GetNamedItem("type");
if(oAssignType != null)
{
sAssignType = oAssignType.Value;
}
sAssign = oAssign.InnerText;
}
string sActionID = oXmlAction.Attributes.GetNamedItem("actionId").InnerText;
string sType = oXmlAction.Attributes.GetNamedItem("type").InnerText;
Action.ActionType oType = ParseActionType(sType);
Action oAction = new Action(sAssignType, sMatch, sAssign, oType, sActionID);
if(sAssignType.CompareTo("object") == 0)
{
XmlNode oXmlNodeAssign = oAssign.FirstChild;
XMLNodeWrapper oWrapped = new XMLNodeWrapper(oXmlNodeAssign);
oAction = new Action(sAssignType, sMatch, oWrapped, oType, sActionID);
}
return oAction;
}
private Action.ActionType ParseActionType(string sType)
{
if(sType.ToLower().CompareTo("var") == 0)
{
return Action.ActionType.Var;
}
else if(sType.ToLower().CompareTo("makevar") == 0)
{
return Action.ActionType.MakeVar;
}
else if(sType.ToLower().CompareTo("run") == 0)
{
return Action.ActionType.Run;
}
else if(sType.ToLower().CompareTo("stop") == 0)
{
return Action.ActionType.Stop;
}
else
{
return Action.ActionType.None;
}
}
Trigger Loading#region Trigger Loading
private void LoadTrigger(XmlNode oXmlNode)
{
XmlNodeList oXTriggers = oXmlNode.ChildNodes;
for(int i = 0; i < oXTriggers.Count; i++)
{
XmlNode oXTrigger = oXTriggers[i];
XmlNode oIDAttr = oXTrigger.Attributes.GetNamedItem("id");
XmlNode oVarIDAttr = oXTrigger.Attributes.GetNamedItem("varID");
XmlNode oRuleIDAttr = oXTrigger.Attributes.GetNamedItem("ruleID");
string sID = GetAttrValue(oIDAttr);
string sVarID = GetAttrValue(oVarIDAttr);
string sRuleID = GetAttrValue(oRuleIDAttr);
Trigger oTrigger = new Trigger(sID, sVarID, sRuleID);
this.m_oRulesEngine.AddTrigger(oTrigger);
}
}
#endregion
private string GetAttrValue(XmlNode oAttrNode)
{
if(oAttrNode != null)
{
return oAttrNode.Value;
}
else
{
return "";
}
}
Properties#region Properties
/**//// <summary>
/// The RulesEngine object to use with your project.
/// </summary>
public RulesEngine NewRulesEngine
{
get
{
return this.m_oRulesEngine;
}
set
{
this.m_oRulesEngine = value;
}
}
#endregion
<Rule id="CalculateTotalPricing" useInferencing="False">
<Expression />
<Actions>
<Action actionId="StarTotalPrice" type="var">
<Match />
<Assign>(StarQty * StarPrice) - ((StarQty * StarPrice) * StarQtyDiscount)</Assign>
</Action>
由此可以看出,读写功能都是基于结构化维护XML配置文件的,根据配置文件的格式把定制文件内的信息读入到引擎中,与给真实引擎添加燃料不同,从程序上讲就是用适当的业务逻辑制定引擎的具体运转方式,以便在驱动事件运行时根据业务逻辑作出正确的判断,以及按照要求运算和处理数据。随之而来的就是对业务规则对象的定义
<Expression />
<Actions>
<Action actionId="StarTotalPrice" type="var">
<Match />
<Assign>(StarQty * StarPrice) - ((StarQty * StarPrice) * StarQtyDiscount)</Assign>
</Action>
[Serializable]
public class RuleObject: IComparable
{
Instance data members#region Instance data members
/**//// <summary>
/// Id of this Rule
/// </summary>
string m_sID = "";
/**//// <summary>
/// Expression of this Rule
/// </summary>
string m_sExpression = "";
/**//// <summary>
/// Action collection
/// </summary>
ArrayList m_oActionList = new ArrayList();
/**//// <summary>
/// Sub-Rule collection
/// </summary>
SubRulesCollection m_oSubRules = new SubRulesCollection();
/**//// <summary>
/// Bool if this Rule is to be used in Inferencing
/// </summary>
bool m_bUseInferencing = false;
/**//// <summary>
/// Parent Rule of this Rule.
/// </summary>
RuleObject m_oParentRule = null;
#endregion
Properties#region Properties
/**//// <summary>
/// Property for the existence of an Expression in this RuleObject.
/// </summary>
public bool HasExpression
{
get
{
if(this.m_sExpression == "")
{
return false;
}
else
{
return true;
}
}
}
/**//// <summary>
/// Sets the bool for using inferencing.
/// </summary>
public bool UseInferencing
{
get
{
return m_bUseInferencing;
}
set
{
m_bUseInferencing = value;
foreach(RuleObject oRule in this.m_oSubRules)
{
oRule.UseInferencing = value;
}
}
}
/**//// <summary>
/// If any Rules have been added to this RuleObject then it is True, False otherwise
/// </summary>
public bool HasSubRules
{
get
{
return m_oSubRules.HasRules;
}
}
/**//// <summary>
/// The ID that this RuleObject is referenced by. Without an ID this RuleObject cannot be found.
/// </summary>
public string ID
{
get
{
return m_sID;
}
set
{
m_sID = value;
}
}
/**//// <summary>
/// The expression this RuleObject will use.
/// </summary>
public string Expression
{
get
{
return m_sExpression;
}
set
{
m_sExpression = value;
}
}
/**//// <summary>
/// Get the containing RuleObject of this RuleObject.
/// </summary>
public RuleObject ParentRuleObject
{
get
{
return m_oParentRule;
}
set
{
m_oParentRule = value;
}
}
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public RuleObject()
{
}
/**//// <summary>
/// Constructor with ID and Expression
/// </summary>
/// <param name="sID">The ID for this RuleObject</param>
/// <param name="sExpression">The expression for this RuleObject</param>
public RuleObject(string sID, string sExpression)
{
m_sID = sID;
m_sExpression = sExpression;
}
#endregion
Publics#region Publics
/**//// <summary>
/// Concatenates and returns the RuleID Path for this Rule
/// </summary>
/// <returns>string of concatenation of the Rule path of this rule (dot delmited)</returns>
public string GetPathID()
{
if(this.m_oParentRule != null)
{
StringBuilder oSb = new StringBuilder();
RuleObject oParent = this.m_oParentRule;
while(oParent != null)
{
oSb.Insert(0, oParent.ID + ".");
oParent = oParent.ParentRuleObject;
}
oSb.Append(m_sID);
return oSb.ToString();
}
else
{
return this.m_sID;
}
}
#endregion
Action Collection management#region Action Collection management
/**//// <summary>
/// Adds a Action object to this RuleObject
/// </summary>
/// <param name="oAction">The action object</param>
public void AddAction(Action oAction)
{
if(oAction.TypeAction == Action.ActionType.Var)
{
//Inference oInf = new Inference(
}
m_oActionList.Add(oAction);
}
/**//// <summary>
/// Get the number of Actions this RuleObject has
/// </summary>
/// <returns>The count of the Actions for this RuleObject</returns>
public int GetActionCount()
{
return m_oActionList.Count;
}
#endregion
Indexer#region Indexer
/**//// <summary>
/// Indexer for Actions in this RuleObject. (Index based)
/// </summary>
public Action this[int i]
{
get
{
return (Action) m_oActionList[i];
}
set
{
m_oActionList[i] = value;
}
}
#endregion
IComparable Members#region IComparable Members
/**//// <summary>
/// The CompareTo method is used to Sort the RuleObject Collection. The ID is the Compareto object.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
if(obj is RuleObject)
{
RuleObject oRule = (RuleObject) obj;
return String.Compare(m_sID, oRule.ID, true);
}
else if(obj is string)
{
string sObj = obj.ToString();
return String.Compare(m_sID, sObj, true);
}
else
{
return -1;
}
}
#endregion
Sub-RuleObject Mangement#region Sub-RuleObject Mangement
/**//// <summary>
/// Adds a RuleObject to this RuleObject
/// </summary>
/// <param name="oRule"></param>
/// <returns></returns>
public void AddSubRule(RuleObject oRule)
{
oRule.ParentRuleObject = this;
oRule.UseInferencing = this.m_bUseInferencing;
m_oSubRules.Add(oRule);
}
/**//// <summary>
/// Remove a RuleObject from this RuleObject by Index.
/// </summary>
/// <param name="iIndex"></param>
public void RemoveRule(int iIndex)
{
try
{
m_oSubRules.RemoveAt(iIndex);
}
catch
{
}
}
/**//// <summary>
/// The number of SubRules this RuleObject has. 0 is there are none.
/// </summary>
/// <returns></returns>
public int GetSubRuleCount()
{
return m_oSubRules.Count;
}
/**//// <summary>
/// Gets the RuleObject from the SubRules collection that corresponds to the index.
/// </summary>
/// <param name="iIndex"></param>
/// <returns></returns>
public RuleObject GetSubRule(int iIndex)
{
RuleObject oRule = m_oSubRules[iIndex];
if(oRule != null)
{
return oRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Gets Rule contained in the Sub-Rules of this Rule
/// </summary>
/// <param name="sRuleID">Id of Rule to find</param>
/// <returns>The found Rule</returns>
public RuleObject GetSubRule(string sRuleID)
{
RuleObject oRule = m_oSubRules[sRuleID];
if(oRule != null)
{
return oRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Replace a SubRule with the supplied RuleObject
/// </summary>
/// <param name="iIndex"></param>
/// <param name="oRule"></param>
public void SetRule(int iIndex, RuleObject oRule)
{
RuleObject oFoundRule = m_oSubRules[iIndex];
if(oRule != null)
{
m_oSubRules[iIndex] = oFoundRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Replaces the Rule identified by Id with another Rule
/// </summary>
/// <param name="sRuleID">Rule Id to replace</param>
/// <param name="oRule">new Rule for this ID</param>
public void SetRule(string sRuleID, RuleObject oRule)
{
RuleObject oFoundRule = m_oSubRules[sRuleID];
if(oRule != null)
{
m_oSubRules[sRuleID] = oFoundRule;
}
else
{
throw new RuleNotFoundException();
}
}
#endregion
}
同RULE同在的就是ACTION,不一定每个RULE都含有ACTION,但没有ACTION的RULE是模型而不是实体所以:
public class RuleObject: IComparable
{
Instance data members#region Instance data members
/**//// <summary>
/// Id of this Rule
/// </summary>
string m_sID = "";
/**//// <summary>
/// Expression of this Rule
/// </summary>
string m_sExpression = "";
/**//// <summary>
/// Action collection
/// </summary>
ArrayList m_oActionList = new ArrayList();
/**//// <summary>
/// Sub-Rule collection
/// </summary>
SubRulesCollection m_oSubRules = new SubRulesCollection();
/**//// <summary>
/// Bool if this Rule is to be used in Inferencing
/// </summary>
bool m_bUseInferencing = false;
/**//// <summary>
/// Parent Rule of this Rule.
/// </summary>
RuleObject m_oParentRule = null;
#endregion
Properties#region Properties
/**//// <summary>
/// Property for the existence of an Expression in this RuleObject.
/// </summary>
public bool HasExpression
{
get
{
if(this.m_sExpression == "")
{
return false;
}
else
{
return true;
}
}
}
/**//// <summary>
/// Sets the bool for using inferencing.
/// </summary>
public bool UseInferencing
{
get
{
return m_bUseInferencing;
}
set
{
m_bUseInferencing = value;
foreach(RuleObject oRule in this.m_oSubRules)
{
oRule.UseInferencing = value;
}
}
}
/**//// <summary>
/// If any Rules have been added to this RuleObject then it is True, False otherwise
/// </summary>
public bool HasSubRules
{
get
{
return m_oSubRules.HasRules;
}
}
/**//// <summary>
/// The ID that this RuleObject is referenced by. Without an ID this RuleObject cannot be found.
/// </summary>
public string ID
{
get
{
return m_sID;
}
set
{
m_sID = value;
}
}
/**//// <summary>
/// The expression this RuleObject will use.
/// </summary>
public string Expression
{
get
{
return m_sExpression;
}
set
{
m_sExpression = value;
}
}
/**//// <summary>
/// Get the containing RuleObject of this RuleObject.
/// </summary>
public RuleObject ParentRuleObject
{
get
{
return m_oParentRule;
}
set
{
m_oParentRule = value;
}
}
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public RuleObject()
{
}
/**//// <summary>
/// Constructor with ID and Expression
/// </summary>
/// <param name="sID">The ID for this RuleObject</param>
/// <param name="sExpression">The expression for this RuleObject</param>
public RuleObject(string sID, string sExpression)
{
m_sID = sID;
m_sExpression = sExpression;
}
#endregion
Publics#region Publics
/**//// <summary>
/// Concatenates and returns the RuleID Path for this Rule
/// </summary>
/// <returns>string of concatenation of the Rule path of this rule (dot delmited)</returns>
public string GetPathID()
{
if(this.m_oParentRule != null)
{
StringBuilder oSb = new StringBuilder();
RuleObject oParent = this.m_oParentRule;
while(oParent != null)
{
oSb.Insert(0, oParent.ID + ".");
oParent = oParent.ParentRuleObject;
}
oSb.Append(m_sID);
return oSb.ToString();
}
else
{
return this.m_sID;
}
}
#endregion
Action Collection management#region Action Collection management
/**//// <summary>
/// Adds a Action object to this RuleObject
/// </summary>
/// <param name="oAction">The action object</param>
public void AddAction(Action oAction)
{
if(oAction.TypeAction == Action.ActionType.Var)
{
//Inference oInf = new Inference(
}
m_oActionList.Add(oAction);
}
/**//// <summary>
/// Get the number of Actions this RuleObject has
/// </summary>
/// <returns>The count of the Actions for this RuleObject</returns>
public int GetActionCount()
{
return m_oActionList.Count;
}
#endregion
Indexer#region Indexer
/**//// <summary>
/// Indexer for Actions in this RuleObject. (Index based)
/// </summary>
public Action this[int i]
{
get
{
return (Action) m_oActionList[i];
}
set
{
m_oActionList[i] = value;
}
}
#endregion
IComparable Members#region IComparable Members
/**//// <summary>
/// The CompareTo method is used to Sort the RuleObject Collection. The ID is the Compareto object.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
if(obj is RuleObject)
{
RuleObject oRule = (RuleObject) obj;
return String.Compare(m_sID, oRule.ID, true);
}
else if(obj is string)
{
string sObj = obj.ToString();
return String.Compare(m_sID, sObj, true);
}
else
{
return -1;
}
}
#endregion
Sub-RuleObject Mangement#region Sub-RuleObject Mangement
/**//// <summary>
/// Adds a RuleObject to this RuleObject
/// </summary>
/// <param name="oRule"></param>
/// <returns></returns>
public void AddSubRule(RuleObject oRule)
{
oRule.ParentRuleObject = this;
oRule.UseInferencing = this.m_bUseInferencing;
m_oSubRules.Add(oRule);
}
/**//// <summary>
/// Remove a RuleObject from this RuleObject by Index.
/// </summary>
/// <param name="iIndex"></param>
public void RemoveRule(int iIndex)
{
try
{
m_oSubRules.RemoveAt(iIndex);
}
catch
{
}
}
/**//// <summary>
/// The number of SubRules this RuleObject has. 0 is there are none.
/// </summary>
/// <returns></returns>
public int GetSubRuleCount()
{
return m_oSubRules.Count;
}
/**//// <summary>
/// Gets the RuleObject from the SubRules collection that corresponds to the index.
/// </summary>
/// <param name="iIndex"></param>
/// <returns></returns>
public RuleObject GetSubRule(int iIndex)
{
RuleObject oRule = m_oSubRules[iIndex];
if(oRule != null)
{
return oRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Gets Rule contained in the Sub-Rules of this Rule
/// </summary>
/// <param name="sRuleID">Id of Rule to find</param>
/// <returns>The found Rule</returns>
public RuleObject GetSubRule(string sRuleID)
{
RuleObject oRule = m_oSubRules[sRuleID];
if(oRule != null)
{
return oRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Replace a SubRule with the supplied RuleObject
/// </summary>
/// <param name="iIndex"></param>
/// <param name="oRule"></param>
public void SetRule(int iIndex, RuleObject oRule)
{
RuleObject oFoundRule = m_oSubRules[iIndex];
if(oRule != null)
{
m_oSubRules[iIndex] = oFoundRule;
}
else
{
throw new RuleNotFoundException();
}
}
/**//// <summary>
/// Replaces the Rule identified by Id with another Rule
/// </summary>
/// <param name="sRuleID">Rule Id to replace</param>
/// <param name="oRule">new Rule for this ID</param>
public void SetRule(string sRuleID, RuleObject oRule)
{
RuleObject oFoundRule = m_oSubRules[sRuleID];
if(oRule != null)
{
m_oSubRules[sRuleID] = oFoundRule;
}
else
{
throw new RuleNotFoundException();
}
}
#endregion
}
public class Action
{
Instance data members#region Instance data members
/**//// <summary>
/// Assigment expression string
/// </summary>
string m_sAssignStr = "";
/**//// <summary>
/// Target expression string
/// </summary>
string m_sTargetStr = "";
/**//// <summary>
/// Variable Target (uses operator overrides)
/// </summary>
Variable m_oTargetVar = null;
/**//// <summary>
/// Variable Assign (uses operator overrides)
/// </summary>
Variable m_oAssignVar = null;
/**//// <summary>
/// ID of this Action
/// </summary>
string m_sID = "";
/**//// <summary>
/// Public Enum of Action Types
/// </summary>
public enum ActionType
{
/**//// <summary>
/// Nothing occurs from this ActionType
/// </summary>
None,
/**//// <summary>
/// This ActionType is used to "Run" a RuleObject
/// </summary>
Run,
/**//// <summary>
/// This ActionType is used to re-assign a Variable
/// </summary>
Var,
/**//// <summary>
/// This ActionType is used to create a Variable
/// </summary>
MakeVar,
/**//// <summary>
/// This ActionType stops the processing of any more Actions
/// </summary>
Stop
}
/**//// <summary>
/// The ActionType of this Action
/// </summary>
public ActionType m_oType = ActionType.None;
#endregion
Properties#region Properties
/**//// <summary>
/// Property of the ActionType
/// </summary>
public ActionType TypeAction
{
get
{
return m_oType;
}
set
{
m_oType = value;
}
}
/**//// <summary>
/// The ID of this Action
/// </summary>
public string ID
{
get
{
return m_sID;
}
set
{
m_sID = value;
}
}
public string AssignString
{
get
{
return m_sAssignStr;
}
set
{
m_sAssignStr = value;
}
}
public string TargetString
{
get
{
return m_sTargetStr;
}
set
{
m_sTargetStr = value;
}
}
/**//// <summary>
/// The Target value as a Variable object
/// </summary>
public Variable TargetVariable
{
get
{
return m_oTargetVar;
}
set
{
m_oTargetVar = value;
}
}
/**//// <summary>
/// The Assign value as a Variable
/// </summary>
public Variable AssignVariable
{
get
{
return m_oAssignVar;
}
set
{
m_oAssignVar = value;
}
}
/**//// <summary>
/// The Target value as a string
/// </summary>
public object VariableValue
{
set
{
this.m_oTargetVar.StringValue = "" + value;
}
}
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public Action()
{
}
/**//// <summary>
/// Constructor with a know Target Value
/// </summary>
/// <param name="sTargetValue">Value to compare to the result of the RuleObject Expression</param>
public Action(string sTargetValue)
{
m_sTargetStr = sTargetValue;
m_oTargetVar = new Variable("var", sTargetValue);
}
/**//// <summary>
/// Constructor with known values
/// </summary>
/// <remarks>
/// The Assign Type is a string that identifies how to use the action.
/// </remarks>
/// <example>
/// Action oAction = new Action("object", "SomeExpression", "SomeExpression", ActionType.Var, "SomeID");<br/>
/// or<br/>
/// Action oAction = new Action("none", "SomeExpression", "SomeExpression, ActionType.Var, "SomeID");<br/>
/// </example>
/// <param name="sAssignType">The name of the type of this Action</param>
/// <param name="sTargetValue">expression to resolve compare to Rule Expression</param>
/// <param name="sAssignValue">expression to resolve and assign to variable (If ActionType is MakeVar or Var)</param>
/// <param name="oActionType">Type of Action to perform</param>
/// <param name="sID">Either the Variable ID or RuleID depending on the ActionType</param>
public Action(string sAssignType, string sTargetValue, string sAssignValue, ActionType oActionType, string sID)
{
m_sTargetStr = sTargetValue;
m_sAssignStr = sAssignValue;
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", sAssignValue);
m_oAssignVar.StringValue = sAssignValue;
}
/**//// <summary>
/// Constructor with known values
/// </summary>
/// <remarks>
/// The Assign Type is a string that identifies how to use the action.
/// </remarks>
/// <example>
/// Action oAction = new Action("object", "SomeExpression", oSomeObject, ActionType.Var, "SomeID");<br/>
/// </example>
/// <param name="sAssignType"></param>
/// <param name="sTargetValue"></param>
/// <param name="oAssignValue">An Object that implements the IVariableObject Interface</param>
/// <param name="oActionType"></param>
/// <param name="sID"></param>
public Action(string sAssignType, string sTargetValue, IVariableObject oAssignValue, ActionType oActionType, string sID)
{
m_sTargetStr = sTargetValue;
if(sAssignType.ToLower().CompareTo("object") == 0)
{
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", oAssignValue);
}
else
{
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", "" + oAssignValue);
m_oAssignVar.StringValue = "" + oAssignValue;
}
}
#endregion
Publics#region Publics
/**//// <summary>
/// Determines if the Target value is equal to the Var value
/// </summary>
/// <remarks>
/// A Variable is used since the operators are overriden to provide type consistency
/// </remarks>
/// <param name="oVar">Encapulating variable to compare</param>
/// <returns>True if equal and False otherwise</returns>
public bool IsTargetMet(Variable oVar)
{
return m_oTargetVar == oVar;
}
#endregion
Developer#region Developer
/**//// <summary>
/// Returns the developer contact information
/// </summary>
/// <returns>The developer contact information</returns>
public string GetDeveloperInfo()
{
return "Jeff Bramlett, Jaxlab.com - jeff_bramlett@hotmail.com";
}
#endregion
}
与此相同的还有触发事件,变量的定义,并需要一个统一的接口对他们进行查找,调用,和卸载的操作管理,其中每个管理类实例中通过管理一组不重复的ID来映射出类实体。在此就不做详述了{
Instance data members#region Instance data members
/**//// <summary>
/// Assigment expression string
/// </summary>
string m_sAssignStr = "";
/**//// <summary>
/// Target expression string
/// </summary>
string m_sTargetStr = "";
/**//// <summary>
/// Variable Target (uses operator overrides)
/// </summary>
Variable m_oTargetVar = null;
/**//// <summary>
/// Variable Assign (uses operator overrides)
/// </summary>
Variable m_oAssignVar = null;
/**//// <summary>
/// ID of this Action
/// </summary>
string m_sID = "";
/**//// <summary>
/// Public Enum of Action Types
/// </summary>
public enum ActionType
{
/**//// <summary>
/// Nothing occurs from this ActionType
/// </summary>
None,
/**//// <summary>
/// This ActionType is used to "Run" a RuleObject
/// </summary>
Run,
/**//// <summary>
/// This ActionType is used to re-assign a Variable
/// </summary>
Var,
/**//// <summary>
/// This ActionType is used to create a Variable
/// </summary>
MakeVar,
/**//// <summary>
/// This ActionType stops the processing of any more Actions
/// </summary>
Stop
}
/**//// <summary>
/// The ActionType of this Action
/// </summary>
public ActionType m_oType = ActionType.None;
#endregion
Properties#region Properties
/**//// <summary>
/// Property of the ActionType
/// </summary>
public ActionType TypeAction
{
get
{
return m_oType;
}
set
{
m_oType = value;
}
}
/**//// <summary>
/// The ID of this Action
/// </summary>
public string ID
{
get
{
return m_sID;
}
set
{
m_sID = value;
}
}
public string AssignString
{
get
{
return m_sAssignStr;
}
set
{
m_sAssignStr = value;
}
}
public string TargetString
{
get
{
return m_sTargetStr;
}
set
{
m_sTargetStr = value;
}
}
/**//// <summary>
/// The Target value as a Variable object
/// </summary>
public Variable TargetVariable
{
get
{
return m_oTargetVar;
}
set
{
m_oTargetVar = value;
}
}
/**//// <summary>
/// The Assign value as a Variable
/// </summary>
public Variable AssignVariable
{
get
{
return m_oAssignVar;
}
set
{
m_oAssignVar = value;
}
}
/**//// <summary>
/// The Target value as a string
/// </summary>
public object VariableValue
{
set
{
this.m_oTargetVar.StringValue = "" + value;
}
}
#endregion
Ctors#region Ctors
/**//// <summary>
/// Default Constructor
/// </summary>
public Action()
{
}
/**//// <summary>
/// Constructor with a know Target Value
/// </summary>
/// <param name="sTargetValue">Value to compare to the result of the RuleObject Expression</param>
public Action(string sTargetValue)
{
m_sTargetStr = sTargetValue;
m_oTargetVar = new Variable("var", sTargetValue);
}
/**//// <summary>
/// Constructor with known values
/// </summary>
/// <remarks>
/// The Assign Type is a string that identifies how to use the action.
/// </remarks>
/// <example>
/// Action oAction = new Action("object", "SomeExpression", "SomeExpression", ActionType.Var, "SomeID");<br/>
/// or<br/>
/// Action oAction = new Action("none", "SomeExpression", "SomeExpression, ActionType.Var, "SomeID");<br/>
/// </example>
/// <param name="sAssignType">The name of the type of this Action</param>
/// <param name="sTargetValue">expression to resolve compare to Rule Expression</param>
/// <param name="sAssignValue">expression to resolve and assign to variable (If ActionType is MakeVar or Var)</param>
/// <param name="oActionType">Type of Action to perform</param>
/// <param name="sID">Either the Variable ID or RuleID depending on the ActionType</param>
public Action(string sAssignType, string sTargetValue, string sAssignValue, ActionType oActionType, string sID)
{
m_sTargetStr = sTargetValue;
m_sAssignStr = sAssignValue;
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", sAssignValue);
m_oAssignVar.StringValue = sAssignValue;
}
/**//// <summary>
/// Constructor with known values
/// </summary>
/// <remarks>
/// The Assign Type is a string that identifies how to use the action.
/// </remarks>
/// <example>
/// Action oAction = new Action("object", "SomeExpression", oSomeObject, ActionType.Var, "SomeID");<br/>
/// </example>
/// <param name="sAssignType"></param>
/// <param name="sTargetValue"></param>
/// <param name="oAssignValue">An Object that implements the IVariableObject Interface</param>
/// <param name="oActionType"></param>
/// <param name="sID"></param>
public Action(string sAssignType, string sTargetValue, IVariableObject oAssignValue, ActionType oActionType, string sID)
{
m_sTargetStr = sTargetValue;
if(sAssignType.ToLower().CompareTo("object") == 0)
{
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", oAssignValue);
}
else
{
m_oType = oActionType;
m_sID = sID;
m_oTargetVar = new Variable("var", sTargetValue);
this.m_oTargetVar.StringValue = "" + sTargetValue;
m_oAssignVar = new Variable("var", "" + oAssignValue);
m_oAssignVar.StringValue = "" + oAssignValue;
}
}
#endregion
Publics#region Publics
/**//// <summary>
/// Determines if the Target value is equal to the Var value
/// </summary>
/// <remarks>
/// A Variable is used since the operators are overriden to provide type consistency
/// </remarks>
/// <param name="oVar">Encapulating variable to compare</param>
/// <returns>True if equal and False otherwise</returns>
public bool IsTargetMet(Variable oVar)
{
return m_oTargetVar == oVar;
}
#endregion
Developer#region Developer
/**//// <summary>
/// Returns the developer contact information
/// </summary>
/// <returns>The developer contact information</returns>
public string GetDeveloperInfo()
{
return "Jeff Bramlett, Jaxlab.com - jeff_bramlett@hotmail.com";
}
#endregion
}
引擎中最重要的部分我认为是解析器,也可以说是简单的语法分析方法集,在面向对象的设计思路下,就要将他设计成一个类,也就是实现消化输入信息的功能。将在下一篇文章中具体介绍