From fac34c27cd5ad168ce2a07e6667c73fcc7a14ea0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Apr 2013 21:06:55 +0200 Subject: [PATCH] AXmlReader: Fix handling of XML namespaces and CDATA. --- .../Xml/XmlReaderTest.cs | 17 ++- ICSharpCode.NRefactory.Xml/AXmlElement.cs | 2 +- ICSharpCode.NRefactory.Xml/AXmlReader.cs | 143 ++++++++++++++++-- 3 files changed, 142 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs b/ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs index 9220cb5904..feba28b5c8 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs @@ -34,15 +34,16 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml { public static void Run(string fileName) { + bool includeAttributes = true; var textSource = new StringTextSource(File.ReadAllText(fileName)); using (var textReader = textSource.CreateReader()) { using (var xmlReader = new XmlTextReader(textReader)) { - Run(xmlReader); + Run(xmlReader, includeAttributes); } } var doc = new AXmlParser().Parse(textSource); using (var xmlReader = doc.CreateReader()) { - Run(xmlReader); + Run(xmlReader, includeAttributes); } var xmlDocument = new XmlDocument(); xmlDocument.Load(doc.CreateReader()); @@ -62,7 +63,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml "SchemaInfo", "BaseURI", "Settings" }; - public static void Run(XmlReader reader) + public static void Run(XmlReader reader, bool includeAttributes, bool includeAttributeValues = true) { using (StreamWriter output = File.CreateText(Path.Combine(Program.TempPath, reader.GetType().Name + "-output.csv"))) { var properties = typeof(XmlReader).GetProperties(BindingFlags.Public | BindingFlags.Instance) @@ -71,6 +72,16 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml output.WriteLine(CSV(properties.Select(p => p.Name))); do { output.WriteLine(CSV(properties.Select(p => ToString(p.GetValue(reader, null))))); + if (includeAttributes && reader.HasAttributes) { + for (int i = 0; i < reader.AttributeCount; i++) { + reader.MoveToAttribute(i); + output.WriteLine(CSV(properties.Select(p => ToString(p.GetValue(reader, null))))); + if (includeAttributeValues) { + reader.ReadAttributeValue(); + output.WriteLine(CSV(properties.Select(p => ToString(p.GetValue(reader, null))))); + } + } + } } while (reader.Read()); output.WriteLine(CSV(properties.Select(p => ToString(p.GetValue(reader, null))))); } diff --git a/ICSharpCode.NRefactory.Xml/AXmlElement.cs b/ICSharpCode.NRefactory.Xml/AXmlElement.cs index e4ed76ea89..d3c3cdc9f8 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlElement.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlElement.cs @@ -183,7 +183,7 @@ namespace ICSharpCode.NRefactory.Xml var result = new Dictionary(); if (scope == XmlNamespaceScope.All) { result["xml"] = XmlNamespace; - result["xmlns"] = XmlnsNamespace; + //result["xmlns"] = XmlnsNamespace; xmlns should not be included in GetNamespacesInScope() results } for (AXmlElement current = this; current != null; current = current.Parent as AXmlElement) { foreach (var attr in current.Attributes) { diff --git a/ICSharpCode.NRefactory.Xml/AXmlReader.cs b/ICSharpCode.NRefactory.Xml/AXmlReader.cs index 48cd32eee6..de389987a6 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlReader.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlReader.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Xml; using ICSharpCode.NRefactory.Editor; @@ -33,6 +34,7 @@ namespace ICSharpCode.NRefactory.Xml readonly XmlReaderSettings settings; Func offsetToTextLocation; readonly XmlNameTable nameTable; + readonly XmlNamespaceManager nsManager; ReadState readState = ReadState.Initial; XmlNodeType elementNodeType = XmlNodeType.None; IList attributes; @@ -45,6 +47,7 @@ namespace ICSharpCode.NRefactory.Xml this.settings = settings ?? new XmlReaderSettings(); this.offsetToTextLocation = offsetToTextLocation; this.nameTable = this.settings.NameTable ?? new NameTable(); + this.nsManager = new XmlNamespaceManager(this.nameTable); objectIterator.StopAtElementEnd = true; } @@ -77,6 +80,7 @@ namespace ICSharpCode.NRefactory.Xml readState = ReadState.Interactive; return ReadCurrentPosition(); case ReadState.Interactive: + LeaveNode(); objectIterator.MoveInto(); return ReadCurrentPosition(); default: @@ -96,8 +100,10 @@ namespace ICSharpCode.NRefactory.Xml elementNodeType = XmlNodeType.None; return false; } else if (objectIterator.IsAtElementEnd) { - // Don't report EndElement for empty elements - if (!IsEmptyElement) { + if (IsEmptyElement) { + // Don't report EndElement for empty elements + nsManager.PopScope(); + } else { elementNodeType = XmlNodeType.EndElement; return true; } @@ -105,8 +111,17 @@ namespace ICSharpCode.NRefactory.Xml // element start elementNodeType = XmlNodeType.Element; InternalTag startTag = ((InternalTag)obj.NestedObjects[0]); - if (startTag.NestedObjects != null) + nsManager.PushScope(); + if (startTag.NestedObjects != null) { attributes = startTag.NestedObjects.OfType().ToList(); + for (int i = 0; i < attributes.Count; i++) { + var attr = attributes[i]; + if (attr.Name.StartsWith("xmlns:", StringComparison.Ordinal)) + nsManager.AddNamespace(AXmlObject.GetLocalName(attr.Name), attr.Value); + else if (attr.Name == "xmlns") + nsManager.AddNamespace(string.Empty, attr.Value); + } + } return true; } else if (obj is InternalText) { InternalText text = (InternalText)obj; @@ -123,6 +138,17 @@ namespace ICSharpCode.NRefactory.Xml } else if (tag.IsComment && !settings.IgnoreComments) { elementNodeType = XmlNodeType.Comment; return true; + } else if (tag.IsProcessingInstruction && !settings.IgnoreProcessingInstructions) { + if (tag.Name == "xml") { + elementNodeType = XmlNodeType.XmlDeclaration; + attributes = tag.NestedObjects.OfType().ToList(); + } else { + elementNodeType = XmlNodeType.ProcessingInstruction; + } + return true; + } else if (tag.IsCData) { + elementNodeType = XmlNodeType.CDATA; + return true; } else { // TODO all other tags } @@ -133,10 +159,18 @@ namespace ICSharpCode.NRefactory.Xml } } + void LeaveNode() + { + if (elementNodeType == XmlNodeType.EndElement) { + nsManager.PopScope(); + } + } + public override void Skip() { if (readState == ReadState.Interactive) { MoveToElement(); + LeaveNode(); objectIterator.MoveNext(); ReadCurrentPosition(); } @@ -160,7 +194,9 @@ namespace ICSharpCode.NRefactory.Xml get { if (readState != ReadState.Interactive) return string.Empty; - return LookupNamespace(this.Prefix); + if (attributeIndex >= 0 && !inAttributeValue && attributes[attributeIndex].Name == "xmlns") + return AXmlObject.XmlnsNamespace; + return LookupNamespace(this.Prefix) ?? string.Empty; } } @@ -173,14 +209,50 @@ namespace ICSharpCode.NRefactory.Xml return string.Empty; return nameTable.Add(AXmlObject.GetLocalName(attributes[attributeIndex].Name)); } - InternalElement element = objectIterator.CurrentObject as InternalElement; - return element != null ? nameTable.Add(element.LocalName) : string.Empty; + string result; + switch (elementNodeType) { + case XmlNodeType.Element: + case XmlNodeType.EndElement: + result = ((InternalElement)objectIterator.CurrentObject).LocalName; + break; + case XmlNodeType.XmlDeclaration: + result = "xml"; + break; + default: + return string.Empty; + } + return nameTable.Add(result); } } - public override bool IsEmptyElement { + public override string Name { get { if (readState != ReadState.Interactive) + return string.Empty; + if (attributeIndex >= 0) { + if (inAttributeValue) + return string.Empty; + return nameTable.Add(attributes[attributeIndex].Name); + } + string result; + switch (elementNodeType) { + case XmlNodeType.Element: + case XmlNodeType.EndElement: + result = ((InternalElement)objectIterator.CurrentObject).Name; + break; + case XmlNodeType.XmlDeclaration: + result = "xml"; + break; + default: + return string.Empty; + } + return nameTable.Add(result); + } + } + + public override bool IsEmptyElement { + get { + if (readState != ReadState.Interactive || attributeIndex >= 0) return false; InternalElement element = objectIterator.CurrentObject as InternalElement; return element != null && element.NestedObjects.Length == 1; @@ -193,17 +265,51 @@ namespace ICSharpCode.NRefactory.Xml return string.Empty; if (attributeIndex >= 0) return attributes[attributeIndex].Value; - InternalObject currentObject = objectIterator.CurrentObject; - InternalText text = currentObject as InternalText; - if (text == null && currentObject is InternalTag && currentObject.NestedObjects.Length == 1) - text = currentObject.NestedObjects[0] as InternalText; - return text != null ? text.Value : string.Empty; + switch (elementNodeType) { + case XmlNodeType.Text: + case XmlNodeType.Whitespace: + return ((InternalText)objectIterator.CurrentObject).Value; + case XmlNodeType.Comment: + case XmlNodeType.CDATA: + var nestedObjects = objectIterator.CurrentObject.NestedObjects; + if (nestedObjects.Length == 1) + return ((InternalText)nestedObjects[0]).Value; + else + return string.Empty; + case XmlNodeType.XmlDeclaration: + StringBuilder b = new StringBuilder(); + foreach (var attr in objectIterator.CurrentObject.NestedObjects.OfType()) { + if (b.Length > 0) + b.Append(' '); + b.Append(attr.Name); + b.Append('='); + b.Append('"'); + b.Append(attr.Value); + b.Append('"'); + } + return b.ToString(); + default: + return string.Empty; + } } } public override bool HasValue { get { - return !string.IsNullOrEmpty (Value); + if (readState != ReadState.Interactive) + return false; + if (attributeIndex >= 0) + return true; + switch (elementNodeType) { + case XmlNodeType.Text: + case XmlNodeType.Whitespace: + case XmlNodeType.Comment: + case XmlNodeType.XmlDeclaration: + case XmlNodeType.CDATA: + return true; + default: + return false; + } } } @@ -272,7 +378,7 @@ namespace ICSharpCode.NRefactory.Xml if (attributes == null) return -1; for (int i = 0; i < attributes.Count; i++) { - if (AXmlObject.GetLocalName(attributes[i].Name) == name && LookupNamespace(AXmlObject.GetNamespacePrefix(attributes[i].Name)) == ns) + if (AXmlObject.GetLocalName(attributes[i].Name) == name && (LookupNamespace(AXmlObject.GetNamespacePrefix(attributes[i].Name)) ?? string.Empty) == ns) return i; } return -1; @@ -290,7 +396,7 @@ namespace ICSharpCode.NRefactory.Xml public override string LookupNamespace(string prefix) { - return string.Empty; // TODO implement namespace lookup + return nsManager.LookupNamespace(prefix); } public override string GetAttribute(int i) @@ -315,7 +421,12 @@ namespace ICSharpCode.NRefactory.Xml } public override int Depth { - get { return objectIterator.Depth; } + get { + if (attributeIndex < 0) + return objectIterator.Depth; + else + return objectIterator.Depth + (inAttributeValue ? 2 : 1); + } } public override void Close()