using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using CLab.Exceptions;
using CLab.Auxiliary;
using System.IO;
using System.Reflection;
using CLab.Data;
namespace CLab
{
///
/// Parses and validates the input XML file.
/// Inserts types, variables and rules to the set CP object
///
public class ClabXmlParser
{
private CP cpObject;
private XmlReader schemaReader;
private XmlReader reader;
private XmlReaderSettings settings;
public ClabXmlParser(CP cpObject)
{
this.cpObject = cpObject;
}
public void InitializeParser()
{
Assembly ase = Assembly.GetExecutingAssembly();
Stream xmlSchema = ase.GetManifestResourceStream("CLab.Auxiliary.clabschema.xsd");
schemaReader = XmlReader.Create(xmlSchema);
XmlSchema schema = XmlSchema.Read(schemaReader, null);
settings = new XmlReaderSettings();
settings.Schemas.Add(schema);
settings.ValidationType = ValidationType.Schema;
}
public void Parse(Stream stream)
{
InitializeParser();
try
{
reader = XmlTextReader.Create(stream, settings);
RunParse();
stream.Close();
reader.Close();
}
catch (ArgumentNullException e)
{
throw new ClabXMLParserException(String.Format("Argument null {0]", stream.ToString(), e));
}
}
public void Parse(String xmlUrl)
{
InitializeParser();
try
{
reader = XmlTextReader.Create(xmlUrl, settings);
RunParse();
reader.Close();
}
catch (System.IO.FileNotFoundException e)
{
throw new ClabXMLParserException(String.Format("Could not load {0}", e.FileName), e);
}
}
///
/// Parses XML input and builds internal data structure for the problem definition
///
/// Path to the XML file to be parsed
/// Reference to the CP object to insert type, variable and rule/expression objects into
///
private void RunParse()
{
Stack elementStack = new Stack();
Stack typeStack = new Stack();
Stack variableStack = new Stack();
Stack rangeStack = new Stack();
Stack leftExpressionStack = new Stack();
Stack rightExpressionStack = new Stack();
Stack operatorStack = new Stack();
Stack leftOrRight = new Stack();
int depth = -1;
List itemList = new List();
String varName = "";
String lastText = "";
String lastTypeDecl = "";
try
{
/*
* Start reading the xml-file.
*/
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
String elementName = reader.Name;
elementStack.Push(elementName);
if (reader.HasAttributes)
{
for (int i = 0; i < reader.AttributeCount; i++)
{
//Move the attribute to the corresponding stack
reader.MoveToAttribute(i);
if (elementName.Equals("typeDecl"))
typeStack.Push(reader.Value);
else if (elementName.Equals("varDecl"))
variableStack.Push(reader.Value);
else if (elementName.Equals("range"))
rangeStack.Push(reader.Value);
}
}
/*
* The leftOrRight-stack is kept in order to keep track of which side of
* an operator we are currently on
*/
else if (elementName.Equals("left"))
{
depth++;
leftOrRight.Push("left");
}
else if (elementName.Equals("right"))
{
String dump = (String)leftOrRight.Pop();
leftOrRight.Push("right");
}
break;
/*
*All major processing is done when we reach an end tag
*/
case XmlNodeType.EndElement:
String eName = (String)elementStack.Pop();
switch (eName)
{
case "description":
cpObject.Description = lastText;
break;
case "author":
cpObject.Author = lastText;
break;
case "date":
cpObject.Date = lastText;
break;
case "typeVar":
itemList.Add(lastText);
lastTypeDecl = "typeVar";
break;
case "range":
lastTypeDecl = "range";
break;
case "typeDecl":
if (lastTypeDecl.Equals("typeVar"))
cpObject.AddType(new TypeEnum((String)typeStack.Pop(), itemList));
else if (lastTypeDecl.Equals("range"))
{
int endOfRange = Convert.ToInt32((String)rangeStack.Pop());
int startOfRange = Convert.ToInt32((String)rangeStack.Pop());
cpObject.AddType(new TypeRange((String)typeStack.Pop(), startOfRange, endOfRange));
}
itemList = new List();
break;
case "varName":
varName = lastText;
break;
case "varDecl":
cpObject.AddVariable(new Variable((String)variableStack.Pop(), varName));
itemList = new List();
break;
/*
* If the id/integer element is part of a compound expression, insert it in the stack corresponding
* to the correct side of the operator.
* Else default to the left side (only valid when used as the only expression in a rule)
*/
case "id":
case "integer":
Expression exp;
if (eName.Equals("id"))
exp = new ExpressionId(lastText);
else
exp = new ExpressionInt(Convert.ToInt32(lastText));
if (leftOrRight.Count > 0)
{
if (leftOrRight.Peek().Equals("left"))
leftExpressionStack.Push(exp);
else if (leftOrRight.Peek().Equals("right"))
rightExpressionStack.Push(exp);
else
throw new ClabInvalidExpressionException(1, lastText);
}
else
{
leftExpressionStack.Push(exp);
}
break;
case "operator":
operatorStack.Push(Oper2type(lastText));
break;
/*
* We have a complete compound expression.
* Build a new expression with the corresponding components from the stacks and insert the new expression
* in the stack corresponding to the correct side of the outer expression.
*/
case "right":
String dump = (String)leftOrRight.Pop();
if (leftOrRight.Count > 0)
{
if (leftOrRight.Peek().Equals("left") & depth > 0)
leftExpressionStack.Push(new ExpressionBinary((Expression)leftExpressionStack.Pop(), (Common.ExprType)operatorStack.Pop(), (Expression)rightExpressionStack.Pop()));
else if (leftOrRight.Peek().Equals("right") & depth > 0)
rightExpressionStack.Push(new ExpressionBinary((Expression)leftExpressionStack.Pop(), (Common.ExprType)operatorStack.Pop(), (Expression)rightExpressionStack.Pop()));
else
throw new ClabInvalidExpressionException(3, "");
}
depth--;
break;
/*
* If we have a negated expression, take the last expression in the stack and insert it in a new expression
* of type et_neg (- operator) or et_not (! operator) and put it back in the stack
*/
case "neg":
case "not":
Common.ExprType type;
if (eName.Equals("neg"))
type = Common.ExprType.et_neg;
else
type = Common.ExprType.et_not;
if (leftOrRight.Count > 0)
{
if (leftOrRight.Peek().Equals("left"))
leftExpressionStack.Push(new ExpressionNotNeg(type, (Expression)leftExpressionStack.Pop()));
else if (leftOrRight.Peek().Equals("right"))
rightExpressionStack.Push(new ExpressionNotNeg(type, (Expression)rightExpressionStack.Pop()));
}
else
{
if (rightExpressionStack.Count > 0)
leftExpressionStack.Push(new ExpressionNotNeg(type, new ExpressionBinary((Expression)leftExpressionStack.Pop(), (Common.ExprType)operatorStack.Pop(), (Expression)rightExpressionStack.Pop())));
else
leftExpressionStack.Push(new ExpressionNotNeg(type, (Expression)leftExpressionStack.Pop()));
}
break;
/*
* Build the final expression (a complete rule) and insert it in the cp object.
*/
case "ruleDecl":
if (depth != -1)
throw new ClabInvalidExpressionException(4, depth);
if (rightExpressionStack.Count > 0)
cpObject.AddRule(new ExpressionBinary((Expression)leftExpressionStack.Pop(), (Common.ExprType)operatorStack.Pop(), (Expression)rightExpressionStack.Pop()));
else
cpObject.AddRule((Expression)leftExpressionStack.Pop());
break;
default:
break;
}
break;
case XmlNodeType.Text:
lastText = reader.Value;
break;
default:
break;
}
}
}
catch (XmlException e)
{
throw new ClabXMLParserException(String.Format("Failed reading XML file. {0}", e.Message), e);
}
catch (XmlSchemaValidationException e)
{
throw new ClabXMLParserException(String.Format("XML file failed validation. {0}", e.Message), e);
}
catch (InvalidOperationException e)
{
throw new ClabXMLParserException(String.Format("Something bad happened during parsing of rules from the XML file.\n{0}", e.ToString()), e);
}
catch (ClabInvalidExpressionException e)
{
throw new ClabXMLParserException(e.Message, e);
}
}
///
/// String representation of the operator to return an ExprType of
///
Common.ExprType Oper2type(String oper)
{
Common.ExprType setType;
switch (oper)
{
case ">>":
setType = Common.ExprType.et_impl;
break;
case "|":
setType = Common.ExprType.et_or;
break;
case "&":
setType = Common.ExprType.et_and;
break;
case "<=":
setType = Common.ExprType.et_lte;
break;
case ">=":
setType = Common.ExprType.et_gte;
break;
case "<":
setType = Common.ExprType.et_lt;
break;
case ">":
setType = Common.ExprType.et_gt;
break;
case "!=":
setType = Common.ExprType.et_ne;
break;
case "==":
setType = Common.ExprType.et_eq;
break;
case "-":
setType = Common.ExprType.et_minus;
break;
case "+":
setType = Common.ExprType.et_plus;
break;
case "%":
setType = Common.ExprType.et_mod;
break;
case "/":
setType = Common.ExprType.et_div;
break;
case "*":
setType = Common.ExprType.et_times;
break;
default:
throw new ClabXMLParserException("Invalid Expression type");
}
return setType;
}
}
}