private static CharacterSpan GetCharacterSpan(LocationSpan locationSpan, CharacterPositionFinder finder) { var startPos = finder.GetCharacterPosition(locationSpan.Start); var endPos = finder.GetCharacterPosition(locationSpan.End); return(new CharacterSpan(startPos, endPos)); }
public void PrepareTest() { var parentDirectory = Directory.GetParent(new Uri(GetType().Assembly.Location).LocalPath).FullName; var fileName = Path.Combine(parentDirectory, "Resources", "test.xml"); _objectUnderTest = CharacterPositionFinder.CreateFrom(fileName, Encoding.Unicode); }
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; } } }
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 int AdjustHeaderEnd(Container node, CharacterPositionFinder finder, LineInfo position) { var characterPosition = finder.GetCharacterPosition(position); node.HeaderSpan = new CharacterSpan(node.HeaderSpan.Start, characterPosition); return(characterPosition + 1); }
private static int AdjustParentFooter(Container parent, CharacterPositionFinder finder, LineInfo position) { var characterPosition = finder.GetCharacterPosition(position); parent.FooterSpan = new CharacterSpan(characterPosition, parent.FooterSpan.End); return(characterPosition - 1); }
private static int AdjustHeaderToLineEnd(Container node, CharacterPositionFinder finder) { var info = finder.GetLineInfo(node.HeaderSpan.End); var lineLength = finder.GetLineLength(info.LineNumber); var endPosition = new LineInfo(info.LineNumber, lineLength); return(AdjustHeaderEnd(node, finder, endPosition)); }
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()); } }
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 void Fill(File file, CharacterPositionFinder finder) { foreach (var root in file.Children) { var children = root.Children.Where(IsNoAttribute).ToList(); if (children.Any()) { AdjustBegin(root, children, 0, finder); AdjustEnd(root, children, children.Count - 1, finder); } // adjust based on gaps, but only adjust child nodes that are no attributes and no text for (var index = 0; index < children.Count; index++) { var child = children[index]; if (child.Type != NodeType.Text) { AdjustNode(root, children, index, finder); } } } }
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); }
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); } }
private static int AdjustBegin(Container parent, IList <ContainerOrTerminalNode> parentChildren, int indexInParentChildren, CharacterPositionFinder finder) { var node = parentChildren[indexInParentChildren]; int newStartPos; var first = node == parentChildren.First(); if (first) { // first child, so adjust parent's header span and terminal node's line start and span begin newStartPos = parent.HeaderSpan.End + 1; var parentHeaderSpanEnd = finder.GetLineInfo(parent.HeaderSpan.End); if (parentHeaderSpanEnd.LineNumber < node.LocationSpan.Start.LineNumber) { // different lines, so adjust line end of parent newStartPos = AdjustHeaderToLineEnd(parent, finder); } } else { var siblingBefore = parentChildren.ElementAt(indexInParentChildren - 1); newStartPos = siblingBefore.GetTotalSpan().End + 1; var siblingBeforeEndLineNumber = siblingBefore.LocationSpan.End.LineNumber; if (siblingBeforeEndLineNumber != node.LocationSpan.Start.LineNumber) { newStartPos = finder.GetCharacterPosition(new LineInfo(siblingBeforeEndLineNumber + 1, 1)); } } return(newStartPos); }
private static void AdjustNode(Container parent, IList <ContainerOrTerminalNode> parentChildren, int indexInParentChildren, CharacterPositionFinder finder) { var newStartPos = AdjustBegin(parent, parentChildren, indexInParentChildren, finder); var newEndPos = AdjustEnd(parent, parentChildren, indexInParentChildren, finder); // somewhere in the middle, so adjust only node span and location, as well as that from the siblings var newStartLine = finder.GetLineInfo(newStartPos); var newEndLine = finder.GetLineInfo(newEndPos); var node = parentChildren[indexInParentChildren]; node.LocationSpan = new LocationSpan(newStartLine, newEndLine); // now adjust terminal node's start position if (node is Container c) { // only adjust child nodes that are no attributes var children = c.Children.Where(IsNoAttribute).ToList(); if (children.Any()) { c.HeaderSpan = new CharacterSpan(newStartPos, c.HeaderSpan.End); for (var index = 0; index < children.Count; index++) { AdjustNode(c, children, index, finder); } c.FooterSpan = new CharacterSpan(c.FooterSpan.Start, newEndPos); } else { var headerEndLine = finder.GetLineInfo(c.HeaderSpan.End).LineNumber; var footerStartLine = finder.GetLineInfo(c.FooterSpan.Start).LineNumber; if (headerEndLine != footerStartLine) { var endPos = finder.GetLineLength(headerEndLine); var headerEndPos = finder.GetCharacterPosition(headerEndLine, endPos); c.HeaderSpan = new CharacterSpan(newStartPos, headerEndPos); c.FooterSpan = new CharacterSpan(headerEndPos + 1, newEndPos); } else { c.HeaderSpan = new CharacterSpan(newStartPos, c.FooterSpan.Start - 1); c.FooterSpan = new CharacterSpan(c.FooterSpan.Start, newEndPos); } } } else if (node is TerminalNode t) { t.Span = new CharacterSpan(newStartPos, newEndPos); } }
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); }
//// 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 int AdjustEnd(Container parent, IList <ContainerOrTerminalNode> parentChildren, int indexInParentChildren, CharacterPositionFinder finder) { var node = parentChildren[indexInParentChildren]; int newEndPos; var last = node == parentChildren.Last(); if (last) { // last child, so adjust parent's footer span and terminal node's end start and span end newEndPos = parent.FooterSpan.Start - 1; var parentFooterLocation = finder.GetLineInfo(parent.FooterSpan.Start); if (parentFooterLocation.LineNumber != node.LocationSpan.End.LineNumber) { var startPosition = new LineInfo(parentFooterLocation.LineNumber, 1); newEndPos = AdjustParentFooter(parent, finder, startPosition); } } else { var siblingAfter = parentChildren.ElementAt(indexInParentChildren + 1); newEndPos = siblingAfter.GetTotalSpan().Start - 1; var startLine = siblingAfter.LocationSpan.Start.LineNumber; if (startLine != node.LocationSpan.End.LineNumber) { var lineLength = finder.GetLineLength(node.LocationSpan.End); var lineInfo = new LineInfo(node.LocationSpan.End.LineNumber, lineLength); newEndPos = finder.GetCharacterPosition(lineInfo); } } return(newEndPos); }