Browse Source

Fix handling of empty elements in AXmlReader; and add support for comments.

pull/32/merge
Daniel Grunwald 12 years ago
parent
commit
7f24e36cfd
  1. 5
      ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs
  2. 2
      ICSharpCode.NRefactory.Xml/AXmlAttribute.cs
  3. 12
      ICSharpCode.NRefactory.Xml/AXmlDocument.cs
  4. 81
      ICSharpCode.NRefactory.Xml/AXmlElement.cs
  5. 53
      ICSharpCode.NRefactory.Xml/AXmlObject.cs
  6. 40
      ICSharpCode.NRefactory.Xml/AXmlReader.cs
  7. 2
      ICSharpCode.NRefactory.Xml/TagReader.cs

5
ICSharpCode.NRefactory.ConsistencyCheck/Xml/XmlReaderTest.cs

@ -49,6 +49,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml @@ -49,6 +49,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml
xmlDocument.Save(Path.Combine(Program.TempPath, "savedXmlDocument.xml"));
var xDocument = XDocument.Load(doc.CreateReader());
xDocument.Save(Path.Combine(Program.TempPath, "savedXDocument.xml"));
File.WriteAllText(Path.Combine(Program.TempPath, "inputDocument.xml"), textSource.Text);
}
static string CSV(IEnumerable<string> input)
@ -57,8 +58,8 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml @@ -57,8 +58,8 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck.Xml
}
static readonly string[] ignoredProperties = {
"NameTable", "CanResolveEntity", "CanReadBinaryContent", "CanReadValueChunk", "EOF", "ValueType",
"SchemaInfo", "IsDefault", "BaseURI", "Settings"
"NameTable", "CanResolveEntity", "CanReadBinaryContent", "CanReadValueChunk", "ValueType",
"SchemaInfo", "BaseURI", "Settings"
};
public static void Run(XmlReader reader)

2
ICSharpCode.NRefactory.Xml/AXmlAttribute.cs

@ -90,7 +90,7 @@ namespace ICSharpCode.NRefactory.Xml @@ -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
}

12
ICSharpCode.NRefactory.Xml/AXmlDocument.cs

@ -19,6 +19,7 @@ @@ -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 @@ -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/>

81
ICSharpCode.NRefactory.Xml/AXmlElement.cs

@ -20,13 +20,14 @@ using System; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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>

53
ICSharpCode.NRefactory.Xml/AXmlObject.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.Xml @@ -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 @@ -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 @@ -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)
{

40
ICSharpCode.NRefactory.Xml/AXmlReader.cs

@ -20,6 +20,7 @@ using System; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -300,6 +321,7 @@ namespace ICSharpCode.NRefactory.Xml
public override void Close()
{
readState = ReadState.Closed;
offsetToTextLocation = null;
}
public override string BaseURI {

2
ICSharpCode.NRefactory.Xml/TagReader.cs

@ -152,7 +152,7 @@ namespace ICSharpCode.NRefactory.Xml @@ -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();
}
}

Loading…
Cancel
Save