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; } } }