Skip to content

Commit

Permalink
added XSD file && better xml parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
qxsch committed Jun 15, 2015
1 parent 11bb3b5 commit 7b19832
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 25 deletions.
5 changes: 5 additions & 0 deletions BotExamples/BotExamples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
<Name>ChatBot</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="ExampleRules.xml">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
56 changes: 56 additions & 0 deletions BotExamples/ExampleRules.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<ChatBot xmlns="http://www.qxs.ch/ChatBotSchema.xsd">
<Rules>
<Rule Type="BotRule" Name="">
<Pattern><![CDATA[your name is .*]]></Pattern>
<Weight>2</Weight>
<Process><![CDATA[
// Match match;
// ChatSessionInterface session
return "ok, this is my name";
]]></Process>
</Rule>

<Rule Type="RandomAnswersBotRule" Name="">
<Pattern><![CDATA[hi]]></Pattern>
<Weight>3</Weight>
<Messages>
<Message>hi</Message>
<Message>ok</Message>
</Messages>
</Rule>

<Rule Type="PowershellBotRule" Name="pstest">
<Pattern><![CDATA[powershell]]></Pattern>
<Weight>10</Weight>
<Script>
<![CDATA[
( ""Hi from PowerShell "" + $PSVersionTable.PSVersion )
]]>
</Script>
</Rule>

<Rule Type="ReplacementBotRule" Name="">
<Pattern><![CDATA[]]></Pattern>
<Weight>4</Weight>
<Messages>
<Message></Message>
</Messages>
<Setters>
<Set Key="KEY">Value</Set>
<Set Key="KEY">Value</Set>
</Setters>
</Rule>

<Rule Type="ConditionBotRule" Name="">
<Weight>6</Weight>
<Conditions>
<Condition Key="KEY" Operator="Equal">VALUE</Condition>
<Condition Key="KEY" Operator="eq">VALUE</Condition>
</Conditions>
<Rules>
<!-- ... -->
</Rules>
</Rule>
</Rules>
</ChatBot>
9 changes: 8 additions & 1 deletion ChatBot/ChatBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BotResponse.cs" />
<Compile Include="Generators\BotRuleCodeCompiler.cs" />
<Compile Include="MethodExtensions\ChatBotXmlNodeExtensions.cs" />
<Compile Include="Rules\BotRule.cs" />
<Compile Include="ChatBot.cs" />
<Compile Include="ChatBotRuleGenerator.cs" />
<Compile Include="Generators\ChatBotRuleGenerator.cs" />
<Compile Include="ChatSessions\ConsoleChatSession.cs" />
<Compile Include="ChatSessions\ChatSessionInterface.cs" />
<Compile Include="LinkedList.cs" />
Expand All @@ -94,7 +96,12 @@
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="ChatBotXmlSchema.xsd">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
100 changes: 100 additions & 0 deletions ChatBot/ChatBotXmlSchema.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ChatBotSchema"
targetNamespace="http://www.qxs.ch/ChatBotSchema.xsd"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns="http://www.qxs.ch/ChatBotSchema.xsd"
xmlns:mstns="http://www.qxs.ch/ChatBotSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>


<xs:complexType name="Messages">
<xs:choice>
<xs:element name="Message" type="xs:string" minOccurs="1" maxOccurs="unbounded" />
</xs:choice>
</xs:complexType>

<xs:complexType name="Setters">
<xs:choice>
<xs:element name="Set" type="Set" minOccurs="1" maxOccurs="unbounded" />
</xs:choice>
</xs:complexType>

<xs:complexType name="Set">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Key" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<xs:complexType name="Conditions">
<xs:choice>
<xs:element name="Condition" type="Condition" minOccurs="1" maxOccurs="unbounded" />
</xs:choice>
</xs:complexType>

<xs:complexType name="Condition">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Key" type="xs:string" />
<xs:attribute name="Operator" type="ConditionOperator" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<xs:simpleType name="ConditionOperator">
<xs:restriction base="xs:string">
<xs:enumeration value="Equal"/>
<xs:enumeration value="eq"/>

<xs:enumeration value="EqualIgnoreCase"/>
<xs:enumeration value="ieq"/>

<xs:enumeration value="NotEqual"/>
<xs:enumeration value="ne"/>

<xs:enumeration value="NotEqualIgnoreCase"/>
<xs:enumeration value="ine"/>
</xs:restriction>
</xs:simpleType>


<xs:complexType name="Rule">
<xs:sequence>
<xs:element name="Pattern" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="Weight" type="xs:integer" minOccurs="0" maxOccurs="1" />
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="Process" type="xs:string" />
<xs:element name="Script" type="xs:string" />
<xs:sequence>
<xs:element name="Messages" type="Messages" minOccurs="0" maxOccurs="1" />
<xs:element name="Setters" type="Setters" minOccurs="0" maxOccurs="1" />
</xs:sequence>
<xs:sequence>
<xs:element name="Conditions" type="Conditions" minOccurs="0" maxOccurs="1" />
<xs:element name="Rules" type="Rules" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:choice>

</xs:sequence>
<xs:attribute name="Type" type="xs:string" />
<xs:attribute name="Name" type="xs:string" />
</xs:complexType>


<xs:complexType name="Rules">
<xs:choice>
<xs:element name="Rule" type="Rule" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
</xs:complexType>

<xs:element name="ChatBot">
<xs:complexType>
<xs:sequence>
<xs:element name="Rules" type="Rules" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
125 changes: 125 additions & 0 deletions ChatBot/Generators/BotRuleCodeCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace QXS.ChatBot
{
public class BotRuleCodeCompiler
{
public const string DefaultNamspace = "QXS.ChatBot.CompiledBotRuleCode";
public readonly string Code;
public readonly string ClassName;

protected CompilerResults results;
protected MethodInfo method;

public BotRuleCodeCompiler(string code, List<string> AssemblyReferences = null, List<string> NameSpaceUsings = null)
{
ClassName = "Program" + (Guid.NewGuid()).ToString("N"); // just set the pure ClassName

this.Code =
"using System;\n" +
"using System.Text;\n" +
"using System.Text.RegularExpressions;\n" +
"using QXS.ChatBot;\n"

;
if (NameSpaceUsings == null)
{
this.Code +=
"using System.Collections;\n" +
"using System.Collections.Generic;\n" +
"using System.Linq;\n" +
"using System.Reflection;\n"
;
}
else
{
Regex namspaceValidator = new Regex(@"^([a-z]+[a-z0-9]*)(\.[a-z]+[a-z0-9]*)*$", RegexOptions.IgnoreCase);
foreach (string nspace in NameSpaceUsings)
{
if (!namspaceValidator.IsMatch(nspace))
{
throw new ArgumentException("Invalid namespace using for value \"" + nspace + "\"!");
}
this.Code += "using " + nspace + ";\n";
}
}

this.Code += @"
namespace " + DefaultNamspace + @"
{
public class " + ClassName + @"
{
public static string Process(Match match, ChatSessionInterface session)
{
" + code.Replace("\n", "\n ").TrimEnd() + @"
return null;
}
}
}
";
CSharpCodeProvider provider = new CSharpCodeProvider();

CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("mscorlib.dll");
parameters.ReferencedAssemblies.Add("ChatBot.dll");
if (AssemblyReferences == null)
{
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
}
else
{
foreach (string aref in AssemblyReferences)
{
parameters.ReferencedAssemblies.Add(aref);
}
}
// True - memory generation, false - external file generation
parameters.GenerateInMemory = true;
// True - exe file generation, false - dll file generation
parameters.GenerateExecutable = false;

results = provider.CompileAssemblyFromSource(parameters, this.Code);

if (results.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();

foreach (CompilerError error in results.Errors)
{
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}

throw new InvalidOperationException(sb.ToString());
}

ClassName = DefaultNamspace + "." + ClassName; // set the full ClassName
method = results.CompiledAssembly.GetType(ClassName).GetMethod("Process");
}


public string Execute(Match match, ChatSessionInterface session)
{
object result = method.Invoke(null, new object[] { match, session });
if (result == null)
{
return null;
}
return (string)result;
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public string GetRuleName(XmlNode node)

public string GetRulePattern(XmlNode node)
{
foreach (XmlNode subnode in node.SelectNodes("Pattern"))
foreach (XmlNode subnode in node.SelectChatBotNodes("cb:Pattern"))
{
return subnode.InnerText;
}
Expand All @@ -40,7 +40,7 @@ public string GetRulePattern(XmlNode node)
public int GetRuleWeight(XmlNode node)
{
int weight = -100;
foreach (XmlNode subnode in node.SelectNodes("Weight"))
foreach (XmlNode subnode in node.SelectChatBotNodes("cb:Weight"))
{
if (Int32.TryParse(subnode.InnerText.Trim(), out weight))
{
Expand Down Expand Up @@ -133,14 +133,13 @@ public List<BotRule> Parse(XmlReader reader)
return Parse(doc);
}


public List<BotRule> Parse(XmlDocument document, XmlNode startNode=null)
{

List<BotRule> liste = new List<BotRule>();
if (startNode == null)
{
foreach (XmlNode node in document.SelectNodes("/ChatBot/Rules/Rule"))
foreach (XmlNode node in document.SelectChatBotNodes("/cb:ChatBot/cb:Rules/cb:Rule"))
{
BotRule rule = ProcessNode(node);
if (rule != null)
Expand All @@ -151,7 +150,7 @@ public List<BotRule> Parse(XmlDocument document, XmlNode startNode=null)
}
else
{
foreach (XmlNode node in startNode.SelectNodes("Rules/Rule"))
foreach (XmlNode node in startNode.SelectChatBotNodes("cb:Rules/cb:Rule"))
{
BotRule rule = ProcessNode(node);
if (rule != null)
Expand All @@ -164,9 +163,5 @@ public List<BotRule> Parse(XmlDocument document, XmlNode startNode=null)
return liste;
}





}
}
Loading

0 comments on commit 7b19832

Please sign in to comment.