public static File Parse(string filePath, string encoding, IXmlFlavor flavor) { Tracer.Trace($"Using {flavor.GetType().Name} flavor for '{filePath}'."); var encodingToUse = Encoding.GetEncoding(encoding); File file; using (var finder = CharacterPositionFinder.CreateFrom(filePath, encodingToUse)) { file = ParseCore(filePath, finder, flavor, encodingToUse); Resorter.Resort(file); GapFiller.Fill(file, finder); } CommentCleaner.Clean(file); return(file); }
public static File ParseCore(string filePath, CharacterPositionFinder finder, IXmlFlavor flavor, Encoding encoding) { using (var reader = new XmlTextReader(new StreamReader(SystemFile.OpenRead(filePath), encoding))) { var file = new File { Name = filePath, FooterSpan = CharacterSpan.None, // there is no footer }; var fileBegin = new LineInfo(reader.LineNumber + 1, reader.LinePosition); try { var dummyRoot = new Container(); // Parse the XML and display the text content of each of the elements. // as there are XMLs that have the declaration on same line as the root element, we just loop and parse until the end while (!reader.EOF) { Parse(reader, dummyRoot, finder, flavor); } var root = dummyRoot.Children.OfType <Container>().Last(); var rootEnd = FixRoot(root, dummyRoot); var fileEnd = new LineInfo(reader.LineNumber, reader.LinePosition - 1); var positionAfterLastElement = new LineInfo(rootEnd.LineNumber, rootEnd.LinePosition + 1); // we calculate the next one (either a new line character or a regular one) file.LocationSpan = new LocationSpan(fileBegin, fileEnd); if (positionAfterLastElement < fileEnd) { file.FooterSpan = GetCharacterSpan(new LocationSpan(positionAfterLastElement, fileEnd), finder); } file.Children.Add(root); } catch (XmlException ex) { // try to adjust location span to include full file content // but ignore empty files as parsing errors var lines = SystemFile.ReadLines(filePath).Count(); if (lines == 0) { file.LocationSpan = new LocationSpan(LineInfo.None, LineInfo.None); } else { file.ParsingErrors.Add(new ParsingError { ErrorMessage = ex.Message, Location = new LineInfo(ex.LineNumber, ex.LinePosition), }); file.LocationSpan = new LocationSpan(new LineInfo(1, 0), new LineInfo(lines + 1, 0)); } } return(file); } }
//// ATTENTION !!!! SIDE EFFECT AS WE READ FURTHER !!!! private static LocationSpan GetLocationSpanWithParseAttributes(XmlTextReader reader, Container container, CharacterPositionFinder finder, IXmlFlavor flavor) { return(GetLocationSpan(reader, _ => { if (_.HasAttributes) { // we have to read the attributes ParseAttributes(_, container, finder, flavor); } })); }
private static void ParseTerminalNode(XmlTextReader reader, Container parent, CharacterPositionFinder finder, IXmlFlavor flavor) { var name = flavor.GetName(reader); var type = flavor.GetType(reader); var content = flavor.GetContent(reader); var locationSpan = GetLocationSpan(reader); var span = GetCharacterSpan(locationSpan, finder); var child = AddTerminalNode(parent, type, name, content, locationSpan, span); flavor.FinalAdjustAfterParsingComplete(child); }
private static void ParseAttribute(XmlTextReader reader, Container parent, CharacterPositionFinder finder, IXmlFlavor flavor) { var attributeStartPos = new LineInfo(reader.LineNumber, reader.LinePosition); var name = flavor.GetName(reader); var type = flavor.GetType(reader); // important call to be able to read the attribute value reader.ReadAttributeValue(); var content = flavor.GetContent(reader); var attributeEndPos = new LineInfo(reader.LineNumber, reader.LinePosition + content.Length); var startPos = finder.GetCharacterPosition(attributeStartPos); var endPos = finder.GetCharacterPosition(attributeEndPos); var locationSpan = new LocationSpan(attributeStartPos, attributeEndPos); var span = new CharacterSpan(startPos, endPos); var child = AddTerminalNode(parent, type, name, content, locationSpan, span); flavor.FinalAdjustAfterParsingComplete(child); }
private static void ParseAttributes(XmlTextReader reader, Container parent, CharacterPositionFinder finder, IXmlFlavor flavor) { if (flavor.ParseAttributesEnabled) { reader.MoveToFirstAttribute(); // read all attributes do { ParseAttribute(reader, parent, finder, flavor); }while (reader.MoveToNextAttribute()); } }
private static void ParseElement(XmlTextReader reader, Container parent, CharacterPositionFinder finder, IXmlFlavor flavor) { var name = flavor.GetName(reader); var type = flavor.GetType(reader); var content = flavor.GetContent(reader); var container = new Container { Type = type, Name = name, Content = content, }; var isEmpty = reader.IsEmptyElement; var startingSpan = GetLocationSpanWithParseAttributes(reader, container, finder, flavor); var headerSpan = GetCharacterSpan(startingSpan, finder); if (isEmpty) { // there is no content, so we have to get away of the footer by just using the '/>' characters as footer var headerSpanCorrected = new CharacterSpan(headerSpan.Start, Math.Max(headerSpan.Start, headerSpan.End - 2)); var footerSpanCorrected = new CharacterSpan(Math.Max(0, headerSpan.End - 1), headerSpan.End); container.LocationSpan = startingSpan; container.HeaderSpan = headerSpanCorrected; container.FooterSpan = footerSpanCorrected; } else { while (reader.NodeType != XmlNodeType.EndElement) { Parse(reader, container, finder, flavor); // we had a side effect (reading further on stream to get the location span), so we have to check whether we found already an element or end element if (reader.NodeType == XmlNodeType.Element) { continue; } if (reader.NodeType == XmlNodeType.EndElement) { break; } } var endingSpan = GetLocationSpan(reader); var footerSpan = GetCharacterSpan(endingSpan, finder); container.LocationSpan = new LocationSpan(startingSpan.Start, endingSpan.End); container.HeaderSpan = headerSpan; container.FooterSpan = footerSpan; } // check whether we can use a terminal node instead var child = flavor.FinalAdjustAfterParsingComplete(container); parent.Children.Add(child); }
private static void Parse(XmlTextReader reader, Container parent, CharacterPositionFinder finder, IXmlFlavor flavor) { var nodeType = reader.NodeType; switch (nodeType) { case XmlNodeType.Element: { ParseElement(reader, parent, finder, flavor); break; } case XmlNodeType.ProcessingInstruction: case XmlNodeType.Comment: case XmlNodeType.XmlDeclaration: case XmlNodeType.CDATA: case XmlNodeType.Text: { ParseTerminalNode(reader, parent, finder, flavor); break; } default: { reader.Read(); break; } } }