diff options
author | Daniel Grunwald <daniel@danielgrunwald.de> | 2013-04-26 22:17:13 +0400 |
---|---|---|
committer | Daniel Grunwald <daniel@danielgrunwald.de> | 2013-05-08 20:36:55 +0400 |
commit | 7f24e36cfde7ad999e25f0acae367820e547197c (patch) | |
tree | aafa1e1b27dbec92e65803cb2b21322c182e32b0 /ICSharpCode.NRefactory.Xml | |
parent | 6e5560a6b911058ac7022ed6bc0bafd71a95a08c (diff) |
Fix handling of empty elements in AXmlReader; and add support for comments.
Diffstat (limited to 'ICSharpCode.NRefactory.Xml')
-rw-r--r-- | ICSharpCode.NRefactory.Xml/AXmlAttribute.cs | 2 | ||||
-rw-r--r-- | ICSharpCode.NRefactory.Xml/AXmlDocument.cs | 12 | ||||
-rw-r--r-- | ICSharpCode.NRefactory.Xml/AXmlElement.cs | 81 | ||||
-rw-r--r-- | ICSharpCode.NRefactory.Xml/AXmlObject.cs | 53 | ||||
-rw-r--r-- | ICSharpCode.NRefactory.Xml/AXmlReader.cs | 40 | ||||
-rw-r--r-- | ICSharpCode.NRefactory.Xml/TagReader.cs | 2 |
6 files changed, 156 insertions, 34 deletions
diff --git a/ICSharpCode.NRefactory.Xml/AXmlAttribute.cs b/ICSharpCode.NRefactory.Xml/AXmlAttribute.cs index 8af65aff..9fe13856 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlAttribute.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlAttribute.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.NRefactory.Xml AXmlElement elem = this.ParentElement; if (elem != null) { - return elem.ResolvePrefix(this.Prefix); + return elem.LookupNamespace(this.Prefix) ?? NoNamespace; } return NoNamespace; // Orphaned attribute } diff --git a/ICSharpCode.NRefactory.Xml/AXmlDocument.cs b/ICSharpCode.NRefactory.Xml/AXmlDocument.cs index 66a97957..165c67e0 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlDocument.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlDocument.cs @@ -19,6 +19,7 @@ using System; using System.Globalization; using System.Xml; +using ICSharpCode.NRefactory.Editor; namespace ICSharpCode.NRefactory.Xml { @@ -32,16 +33,9 @@ namespace ICSharpCode.NRefactory.Xml { } - /// <inheritdoc/> - public override XmlReader CreateReader() - { - return new AXmlReader(internalObject.NestedObjects); - } - - /// <inheritdoc/> - public override XmlReader CreateReader(Func<int, TextLocation> offsetToTextLocation) + internal override ObjectIterator CreateIteratorForReader() { - return new AXmlReader(internalObject.NestedObjects, startOffset, offsetToTextLocation); + return new ObjectIterator(internalObject.NestedObjects, startOffset); } /// <inheritdoc/> diff --git a/ICSharpCode.NRefactory.Xml/AXmlElement.cs b/ICSharpCode.NRefactory.Xml/AXmlElement.cs index c5efae36..e4ed76ea 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlElement.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlElement.cs @@ -20,13 +20,14 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Xml; namespace ICSharpCode.NRefactory.Xml { /// <summary> /// XML element. /// </summary> - public class AXmlElement : AXmlObject + public class AXmlElement : AXmlObject, IXmlNamespaceResolver { internal AXmlElement(AXmlObject parent, int startOffset, InternalElement internalObject) : base(parent, startOffset, internalObject) @@ -90,7 +91,7 @@ namespace ICSharpCode.NRefactory.Xml /// <summary> The part of name before ":" </summary> /// <returns> Empty string if not found </returns> public string Prefix { - get { return ((InternalElement)internalObject).Prefix; } + get { return ((InternalElement)internalObject).Prefix; } } /// <summary> The part of name after ":" </summary> @@ -104,26 +105,37 @@ namespace ICSharpCode.NRefactory.Xml public string Namespace { get { string prefix = this.Prefix; - return ResolvePrefix(prefix); + return LookupNamespace(prefix); } } - /// <summary> Find the defualt namespace for this context </summary> + /// <summary> Find the default namespace for this context </summary> + [Obsolete("Use LookupNamespace(string.Empty) instead")] public string FindDefaultNamespace() { - return ResolvePrefix(string.Empty); + return LookupNamespace(string.Empty) ?? NoNamespace; } /// <summary> /// Recursively resolve given prefix in this context. Prefix must have some value. /// </summary> /// <returns> Empty string if prefix is not found </returns> + [Obsolete("Use LookupNamespace() instead")] public string ResolvePrefix(string prefix) { + return LookupNamespace(prefix) ?? NoNamespace; + } + + /// <summary> + /// Recursively resolve given prefix in this context. + /// </summary> + /// <returns><c>null</c> if prefix is not found</returns> + public string LookupNamespace(string prefix) + { if (prefix == null) throw new ArgumentNullException("prefix"); - // Implicit namesapces + // Implicit namespaces if (prefix == "xml") return XmlNamespace; if (prefix == "xmlns") return XmlnsNamespace; @@ -134,7 +146,60 @@ namespace ICSharpCode.NRefactory.Xml return attr.Value; } } - return NoNamespace; // Can not find prefix + return null; // Can not find prefix + } + + /// <summary> + /// Gets the prefix that is mapped to the specified namespace URI. + /// </summary> + /// <returns>The prefix that is mapped to the namespace URI; null if the namespace URI is not mapped to a prefix.</returns> + public string LookupPrefix(string namespaceName) + { + if (namespaceName == null) + throw new ArgumentNullException("namespaceName"); + + if (namespaceName == XmlNamespace) + return "xml"; + if (namespaceName == XmlnsNamespace) + return "xmlns"; + for (AXmlElement current = this; current != null; current = current.Parent as AXmlElement) { + foreach (var attr in current.Attributes) { + if (attr.Value == namespaceName) { + if (attr.Name.StartsWith("xmlns:", StringComparison.Ordinal)) + return attr.LocalName; + else if (attr.Name == "xmlns") + return string.Empty; + } + } + } + return null; // Can not find prefix + } + + /// <summary> + /// Gets a collection of defined prefix-namespace mappings that are currently in scope. + /// </summary> + public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) + { + var result = new Dictionary<string, string>(); + if (scope == XmlNamespaceScope.All) { + result["xml"] = XmlNamespace; + result["xmlns"] = XmlnsNamespace; + } + for (AXmlElement current = this; current != null; current = current.Parent as AXmlElement) { + foreach (var attr in current.Attributes) { + if (attr.Name.StartsWith("xmlns:", StringComparison.Ordinal)) { + string prefix = attr.LocalName; + if (!result.ContainsKey(prefix)) { + result.Add(prefix, attr.Value); + } + } else if (attr.Name == "xmlns" && !result.ContainsKey(string.Empty)) { + result.Add(string.Empty, attr.Value); + } + } + if (scope == XmlNamespaceScope.Local) + break; + } + return result; } /// <summary> @@ -144,7 +209,7 @@ namespace ICSharpCode.NRefactory.Xml /// <returns>Null if not found</returns> public string GetAttributeValue(string localName) { - return GetAttributeValue(NoNamespace, localName); + return GetAttributeValue(string.Empty, localName); } /// <summary> diff --git a/ICSharpCode.NRefactory.Xml/AXmlObject.cs b/ICSharpCode.NRefactory.Xml/AXmlObject.cs index 269a54da..c139efd3 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlObject.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlObject.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.Xml public abstract class AXmlObject : ISegment { /// <summary> Empty string. The namespace used if there is no "xmlns" specified </summary> - public static readonly string NoNamespace = string.Empty; + internal static readonly string NoNamespace = string.Empty; /// <summary> Namespace for "xml:" prefix: "http://www.w3.org/XML/1998/namespace" </summary> public static readonly string XmlNamespace = "http://www.w3.org/XML/1998/namespace"; @@ -54,17 +54,58 @@ namespace ICSharpCode.NRefactory.Xml /// <summary> /// Creates an XML reader that reads from this document. /// </summary> - public virtual XmlReader CreateReader() + /// <remarks> + /// The reader will ignore comments and processing instructions; and will not have line information. + /// </remarks> + public XmlReader CreateReader() { - return new AXmlReader(new[] { internalObject }); + return new AXmlReader(CreateIteratorForReader()); } /// <summary> /// Creates an XML reader that reads from this document. /// </summary> - public virtual XmlReader CreateReader(Func<int, TextLocation> offsetToTextLocation) + /// <param name="settings">Reader settings. + /// Currently, only <c>IgnoreComments</c> is supported.</param> + /// <remarks> + /// The reader will not have line information. + /// </remarks> + public XmlReader CreateReader(XmlReaderSettings settings) { - return new AXmlReader(new[] { internalObject }, startOffset, offsetToTextLocation); + return new AXmlReader(CreateIteratorForReader(), settings); + } + + /// <summary> + /// Creates an XML reader that reads from this document. + /// </summary> + /// <param name="settings">Reader settings. + /// Currently, only <c>IgnoreComments</c> is supported.</param> + /// <param name="document"> + /// The document that was used to parse the XML. It is used to convert offsets to line information. + /// </param> + public XmlReader CreateReader(XmlReaderSettings settings, IDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + return new AXmlReader(CreateIteratorForReader(), settings, document.GetLocation); + } + + /// <summary> + /// Creates an XML reader that reads from this document. + /// </summary> + /// <param name="settings">Reader settings. + /// Currently, only <c>IgnoreComments</c> is supported.</param> + /// <param name="offsetToTextLocation"> + /// A function for converting offsets to line information. + /// </param> + public XmlReader CreateReader(XmlReaderSettings settings, Func<int, TextLocation> offsetToTextLocation) + { + return new AXmlReader(CreateIteratorForReader(), settings, offsetToTextLocation); + } + + internal virtual ObjectIterator CreateIteratorForReader() + { + return new ObjectIterator(new[] { internalObject }, startOffset); } /// <summary> @@ -100,7 +141,7 @@ namespace ICSharpCode.NRefactory.Xml /// <summary> /// Gets a child fully containg the given offset. /// Goes recursively down the tree. - /// Specail case if at the end of attribute or text + /// Special case if at the end of attribute or text /// </summary> public AXmlObject GetChildAtOffset(int offset) { diff --git a/ICSharpCode.NRefactory.Xml/AXmlReader.cs b/ICSharpCode.NRefactory.Xml/AXmlReader.cs index ae503861..48cd32ee 100644 --- a/ICSharpCode.NRefactory.Xml/AXmlReader.cs +++ b/ICSharpCode.NRefactory.Xml/AXmlReader.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml; +using ICSharpCode.NRefactory.Editor; namespace ICSharpCode.NRefactory.Xml { @@ -29,18 +30,21 @@ namespace ICSharpCode.NRefactory.Xml sealed class AXmlReader : XmlReader, IXmlLineInfo { readonly ObjectIterator objectIterator; - readonly Func<int, TextLocation> offsetToTextLocation; - readonly XmlNameTable nameTable = new NameTable(); + readonly XmlReaderSettings settings; + Func<int, TextLocation> offsetToTextLocation; + readonly XmlNameTable nameTable; ReadState readState = ReadState.Initial; XmlNodeType elementNodeType = XmlNodeType.None; IList<InternalAttribute> attributes; int attributeIndex = -1; bool inAttributeValue; - internal AXmlReader(InternalObject[] objects, int startPosition = 0, Func<int, TextLocation> offsetToTextLocation = null) + internal AXmlReader(ObjectIterator objectIterator, XmlReaderSettings settings = null, Func<int, TextLocation> offsetToTextLocation = null) { + this.objectIterator = objectIterator; + this.settings = settings ?? new XmlReaderSettings(); this.offsetToTextLocation = offsetToTextLocation; - objectIterator = new ObjectIterator(objects, startPosition); + this.nameTable = this.settings.NameTable ?? new NameTable(); objectIterator.StopAtElementEnd = true; } @@ -53,6 +57,10 @@ namespace ICSharpCode.NRefactory.Xml get { return readState; } } + public override XmlReaderSettings Settings { + get { return settings; } + } + public override bool ReadAttributeValue() { if (attributeIndex >= 0 && !inAttributeValue) { @@ -88,8 +96,11 @@ namespace ICSharpCode.NRefactory.Xml elementNodeType = XmlNodeType.None; return false; } else if (objectIterator.IsAtElementEnd) { - elementNodeType = XmlNodeType.EndElement; - return true; + // Don't report EndElement for empty elements + if (!IsEmptyElement) { + elementNodeType = XmlNodeType.EndElement; + return true; + } } else if (obj is InternalElement) { // element start elementNodeType = XmlNodeType.Element; @@ -106,8 +117,15 @@ namespace ICSharpCode.NRefactory.Xml } return true; } else if (obj is InternalTag) { - // start/end tags can be skipped as the parent InternalElement already handles them, - // TODO all other tags (xml decl, comments, ...) + InternalTag tag = (InternalTag)obj; + if (tag.IsStartOrEmptyTag || tag.IsEndTag) { + // start/end tags can be skipped as the parent InternalElement already handles them + } else if (tag.IsComment && !settings.IgnoreComments) { + elementNodeType = XmlNodeType.Comment; + return true; + } else { + // TODO all other tags + } } else { throw new NotSupportedException(); } @@ -175,7 +193,10 @@ namespace ICSharpCode.NRefactory.Xml return string.Empty; if (attributeIndex >= 0) return attributes[attributeIndex].Value; - InternalText text = objectIterator.CurrentObject as InternalText; + 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; } } @@ -300,6 +321,7 @@ namespace ICSharpCode.NRefactory.Xml public override void Close() { readState = ReadState.Closed; + offsetToTextLocation = null; } public override string BaseURI { diff --git a/ICSharpCode.NRefactory.Xml/TagReader.cs b/ICSharpCode.NRefactory.Xml/TagReader.cs index 49423307..e5cab934 100644 --- a/ICSharpCode.NRefactory.Xml/TagReader.cs +++ b/ICSharpCode.NRefactory.Xml/TagReader.cs @@ -152,7 +152,7 @@ namespace ICSharpCode.NRefactory.Xml }); } else { // Mismatched name - the nesting isn't properly; - // clear the whole stack so that none of the currently open elements are closed as property-nested. + // clear the whole stack so that none of the currently open elements are closed as properly-nested. elementNameStack.Clear(); } } |