Browse Source

Replace AXmlParser with XLinq.

pull/1654/head
Siegfried Pammer 6 years ago
parent
commit
4e5a2c4a6a
  1. 61
      ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs
  2. 28
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 116
      ICSharpCode.Decompiler/Xml/AXmlAttribute.cs
  4. 55
      ICSharpCode.Decompiler/Xml/AXmlDocument.cs
  5. 243
      ICSharpCode.Decompiler/Xml/AXmlElement.cs
  6. 247
      ICSharpCode.Decompiler/Xml/AXmlObject.cs
  7. 142
      ICSharpCode.Decompiler/Xml/AXmlParser.cs
  8. 477
      ICSharpCode.Decompiler/Xml/AXmlReader.cs
  9. 96
      ICSharpCode.Decompiler/Xml/AXmlTag.cs
  10. 63
      ICSharpCode.Decompiler/Xml/AXmlText.cs
  11. 62
      ICSharpCode.Decompiler/Xml/AXmlVisitor.cs
  12. 43
      ICSharpCode.Decompiler/Xml/AnchorMovementType.cs
  13. 69
      ICSharpCode.Decompiler/Xml/ISegment.cs
  14. 218
      ICSharpCode.Decompiler/Xml/ITextSource.cs
  15. 114
      ICSharpCode.Decompiler/Xml/IncrementalParserState.cs
  16. 175
      ICSharpCode.Decompiler/Xml/InternalDocument.cs
  17. 88
      ICSharpCode.Decompiler/Xml/Log.cs
  18. 109
      ICSharpCode.Decompiler/Xml/ObjectIterator.cs
  19. 52
      ICSharpCode.Decompiler/Xml/ReuseEqualityComparer.cs
  20. 160
      ICSharpCode.Decompiler/Xml/StringTextSource.cs
  21. 72
      ICSharpCode.Decompiler/Xml/SyntaxError.cs
  22. 351
      ICSharpCode.Decompiler/Xml/TagMatchingHeuristics.cs
  23. 834
      ICSharpCode.Decompiler/Xml/TagReader.cs
  24. 100
      ICSharpCode.Decompiler/Xml/TextChangeEventArgs.cs
  25. 219
      ICSharpCode.Decompiler/Xml/TextLocation.cs
  26. 47
      ICSharpCode.Decompiler/Xml/TextType.cs
  27. 349
      ICSharpCode.Decompiler/Xml/TokenReader.cs
  28. 47
      ICSharpCode.Decompiler/Xml/XmlSegment.cs
  29. 8
      ILSpy/TextView/XmlDocRenderer.cs

61
ICSharpCode.Decompiler/Xml/DocumentationElement.cs → ICSharpCode.Decompiler/Documentation/XmlDocumentationElement.cs

@ -21,11 +21,12 @@ using System.Collections.Generic; @@ -21,11 +21,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.Xml
namespace ICSharpCode.Decompiler.Documentation
{
/// <summary>
/// Represents an element in the XML documentation.
@ -35,12 +36,10 @@ namespace ICSharpCode.Decompiler.Xml @@ -35,12 +36,10 @@ namespace ICSharpCode.Decompiler.Xml
{
static XmlDocumentationElement Create(string documentationComment, IEntity declaringEntity)
{
var doc = new AXmlParser().Parse(new StringTextSource(documentationComment));
return new XmlDocumentationElement(doc, declaringEntity, null);
return new XmlDocumentationElement(XElement.Parse(documentationComment), declaringEntity, null);
}
readonly AXmlObject xmlObject;
readonly AXmlElement element;
readonly XElement element;
readonly IEntity declaringEntity;
readonly Func<string, IEntity> crefResolver;
volatile string textContent;
@ -53,24 +52,11 @@ namespace ICSharpCode.Decompiler.Xml @@ -53,24 +52,11 @@ namespace ICSharpCode.Decompiler.Xml
/// <summary>
/// Creates a new documentation element.
/// </summary>
public XmlDocumentationElement(AXmlElement element, IEntity declaringEntity, Func<string, IEntity> crefResolver)
public XmlDocumentationElement(XElement element, IEntity declaringEntity, Func<string, IEntity> crefResolver)
{
if (element == null)
throw new ArgumentNullException("element");
this.element = element;
this.xmlObject = element;
this.declaringEntity = declaringEntity;
this.crefResolver = crefResolver;
}
/// <summary>
/// Creates a new documentation element.
/// </summary>
public XmlDocumentationElement(AXmlDocument document, IEntity declaringEntity, Func<string, IEntity> crefResolver)
{
if (document == null)
throw new ArgumentNullException("document");
this.xmlObject = document;
this.declaringEntity = declaringEntity;
this.crefResolver = crefResolver;
}
@ -118,7 +104,7 @@ namespace ICSharpCode.Decompiler.Xml @@ -118,7 +104,7 @@ namespace ICSharpCode.Decompiler.Xml
/// </summary>
public string Name {
get {
return element != null ? element.Name : string.Empty;
return element != null ? element.Name.LocalName : string.Empty;
}
}
@ -127,14 +113,14 @@ namespace ICSharpCode.Decompiler.Xml @@ -127,14 +113,14 @@ namespace ICSharpCode.Decompiler.Xml
/// </summary>
public string GetAttribute(string name)
{
return element != null ? element.GetAttributeValue(name) : string.Empty;
return element != null ? element.Attribute(name).Value : string.Empty;
}
/// <summary>
/// Gets whether this is a pure text node.
/// </summary>
public bool IsTextNode {
get { return xmlObject == null; }
get { return element == null; }
}
/// <summary>
@ -159,11 +145,11 @@ namespace ICSharpCode.Decompiler.Xml @@ -159,11 +145,11 @@ namespace ICSharpCode.Decompiler.Xml
/// </summary>
public IList<XmlDocumentationElement> Children {
get {
if (xmlObject == null)
if (element == null)
return EmptyList<XmlDocumentationElement>.Instance;
return LazyInitializer.EnsureInitialized(
ref this.children,
() => CreateElements(xmlObject.Children, declaringEntity, crefResolver, nestingLevel));
() => CreateElements(element.Nodes(), declaringEntity, crefResolver, nestingLevel));
}
}
@ -172,21 +158,20 @@ namespace ICSharpCode.Decompiler.Xml @@ -172,21 +158,20 @@ namespace ICSharpCode.Decompiler.Xml
"remarks", "returns", "threadsafety", "value"
};
static List<XmlDocumentationElement> CreateElements(IEnumerable<AXmlObject> childObjects, IEntity declaringEntity, Func<string, IEntity> crefResolver, int nestingLevel)
static List<XmlDocumentationElement> CreateElements(IEnumerable<XObject> childObjects, IEntity declaringEntity, Func<string, IEntity> crefResolver, int nestingLevel)
{
List<XmlDocumentationElement> list = new List<XmlDocumentationElement>();
foreach (var child in childObjects) {
var childText = child as AXmlText;
var childTag = child as AXmlTag;
var childElement = child as AXmlElement;
var childText = child as XText;
var childTag = child as XCData;
var childElement = child as XElement;
if (childText != null) {
list.Add(new XmlDocumentationElement(childText.Value, declaringEntity));
} else if (childTag != null && childTag.IsCData) {
foreach (var text in childTag.Children.OfType<AXmlText>())
list.Add(new XmlDocumentationElement(text.Value, declaringEntity));
} else if (childTag != null) {
list.Add(new XmlDocumentationElement(childTag.Value, declaringEntity));
} else if (childElement != null) {
if (nestingLevel < 5 && childElement.Name == "inheritdoc") {
string cref = childElement.GetAttributeValue("cref");
string cref = childElement.Attribute("cref").Value;
IEntity inheritedFrom = null;
string inheritedDocumentation = null;
if (cref != null) {
@ -204,20 +189,20 @@ namespace ICSharpCode.Decompiler.Xml @@ -204,20 +189,20 @@ namespace ICSharpCode.Decompiler.Xml
}
if (inheritedDocumentation != null) {
var doc = new AXmlParser().Parse(new StringTextSource(inheritedDocumentation));
var doc = XDocument.Parse(inheritedDocumentation);
// XPath filter not yet implemented
if (childElement.Parent is AXmlDocument && childElement.GetAttributeValue("select") == null) {
if (childElement.Parent == null && childElement.Attribute("select").Value == null) {
// Inheriting documentation at the root level
List<string> doNotInherit = new List<string>();
doNotInherit.Add("overloads");
doNotInherit.AddRange(childObjects.OfType<AXmlElement>().Select(e => e.Name).Intersect(
doNotInherit.AddRange(childObjects.OfType<XElement>().Select(e => e.Name.LocalName).Intersect(
doNotInheritIfAlreadyPresent));
var inheritedChildren = doc.Children.Where(
var inheritedChildren = doc.Nodes().Where(
inheritedObject => {
AXmlElement inheritedElement = inheritedObject as AXmlElement;
return !(inheritedElement != null && doNotInherit.Contains(inheritedElement.Name));
XElement inheritedElement = inheritedObject as XElement;
return !(inheritedElement != null && doNotInherit.Contains(inheritedElement.Name.LocalName));
});
list.AddRange(CreateElements(inheritedChildren, inheritedFrom, crefResolver, nestingLevel + 1));

28
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
<Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="Documentation\XmlDocumentationElement.cs" />
<Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />
@ -588,33 +589,6 @@ @@ -588,33 +589,6 @@
<Compile Include="Util\ReferenceComparer.cs" />
<Compile Include="Util\TreeTraversal.cs" />
<Compile Include="Util\UnionFind.cs" />
<Compile Include="Xml\AnchorMovementType.cs" />
<Compile Include="Xml\AXmlAttribute.cs" />
<Compile Include="Xml\AXmlDocument.cs" />
<Compile Include="Xml\AXmlElement.cs" />
<Compile Include="Xml\AXmlObject.cs" />
<Compile Include="Xml\AXmlParser.cs" />
<Compile Include="Xml\AXmlReader.cs" />
<Compile Include="Xml\AXmlTag.cs" />
<Compile Include="Xml\AXmlText.cs" />
<Compile Include="Xml\AXmlVisitor.cs" />
<Compile Include="Xml\DocumentationElement.cs" />
<Compile Include="Xml\IncrementalParserState.cs" />
<Compile Include="Xml\InternalDocument.cs" />
<Compile Include="Xml\ISegment.cs" />
<Compile Include="Xml\ITextSource.cs" />
<Compile Include="Xml\Log.cs" />
<Compile Include="Xml\ObjectIterator.cs" />
<Compile Include="Xml\ReuseEqualityComparer.cs" />
<Compile Include="Xml\StringTextSource.cs" />
<Compile Include="Xml\SyntaxError.cs" />
<Compile Include="Xml\TagMatchingHeuristics.cs" />
<Compile Include="Xml\TagReader.cs" />
<Compile Include="Xml\TextChangeEventArgs.cs" />
<Compile Include="Xml\TextLocation.cs" />
<Compile Include="Xml\TextType.cs" />
<Compile Include="Xml\TokenReader.cs" />
<Compile Include="Xml\XmlSegment.cs" />
<None Include="ICSharpCode.Decompiler.nuspec" DependentUpon="ICSharpCode.Decompiler.nuspec.template" />
<None Include="ICSharpCode.Decompiler.nuspec.template" />
<None Include="ICSharpCode.Decompiler.ruleset" />

116
ICSharpCode.Decompiler/Xml/AXmlAttribute.cs

@ -1,116 +0,0 @@ @@ -1,116 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Globalization;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Name-value pair in a tag
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
public class AXmlAttribute : AXmlObject
{
internal AXmlAttribute(AXmlObject parent, int startOffset, InternalAttribute internalObject)
: base(parent, startOffset, internalObject)
{
}
internal InternalAttribute InternalAttribute {
get { return (InternalAttribute)internalObject; }
}
/// <summary> Name with namespace prefix - exactly as in source file </summary>
public string Name { get { return InternalAttribute.Name; } }
/// <summary> Unquoted and dereferenced value of the attribute </summary>
public string Value { get { return InternalAttribute.Value; } }
/// <summary>Gets the segment for the attribute name</summary>
public ISegment NameSegment {
get { return new XmlSegment(startOffset, startOffset + Name.Length); }
}
/// <summary>Gets the segment for the attribute value, including the quotes</summary>
public ISegment ValueSegment {
get { return new XmlSegment(startOffset + Name.Length + InternalAttribute.EqualsSignLength, this.EndOffset); }
}
/// <summary> The element containing this attribute </summary>
/// <returns> Null if orphaned </returns>
public AXmlElement ParentElement {
get {
AXmlTag tag = this.Parent as AXmlTag;
if (tag != null) {
return tag.Parent as AXmlElement;
}
return null;
}
}
/// <summary> The part of name before ":"</summary>
/// <returns> Empty string if not found </returns>
public string Prefix {
get {
return GetNamespacePrefix(this.Name);
}
}
/// <summary> The part of name after ":" </summary>
/// <returns> Whole name if ":" not found </returns>
public string LocalName {
get {
return GetLocalName(this.Name);
}
}
/// <summary>
/// Resolved namespace of the name. Empty string if not found
/// From the specification: "The namespace name for an unprefixed attribute name always has no value."
/// </summary>
public string Namespace {
get {
if (string.IsNullOrEmpty(this.Prefix)) return NoNamespace;
AXmlElement elem = this.ParentElement;
if (elem != null) {
return elem.LookupNamespace(this.Prefix) ?? NoNamespace;
}
return NoNamespace; // Orphaned attribute
}
}
/// <summary> Attribute is declaring namespace ("xmlns" or "xmlns:*") </summary>
public bool IsNamespaceDeclaration {
get {
return this.Name == "xmlns" || this.Prefix == "xmlns";
}
}
/// <inheritdoc/>
public override void AcceptVisitor(AXmlVisitor visitor)
{
visitor.VisitAttribute(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}={2}']", base.ToString(), this.Name, this.Value);
}
}
}

55
ICSharpCode.Decompiler/Xml/AXmlDocument.cs

@ -1,55 +0,0 @@ @@ -1,55 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Globalization;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// The root object of the XML document
/// </summary>
public class AXmlDocument : AXmlObject
{
internal AXmlDocument(AXmlObject parent, int startOffset, InternalDocument internalObject)
: base(parent, startOffset, internalObject)
{
}
internal override ObjectIterator CreateIteratorForReader()
{
return new ObjectIterator(internalObject.NestedObjects, startOffset);
}
/// <inheritdoc/>
public override void AcceptVisitor(AXmlVisitor visitor)
{
visitor.VisitDocument(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} Chld:{1}]", base.ToString(), this.Children.Count);
}
/// <summary>
/// Represents an empty document.
/// </summary>
public readonly static AXmlDocument Empty = new AXmlDocument(null, 0, new InternalDocument());
}
}

243
ICSharpCode.Decompiler/Xml/AXmlElement.cs

@ -1,243 +0,0 @@ @@ -1,243 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// XML element.
/// </summary>
public class AXmlElement : AXmlObject, IXmlNamespaceResolver
{
internal AXmlElement(AXmlObject parent, int startOffset, InternalElement internalObject)
: base(parent, startOffset, internalObject)
{
Log.Assert(internalObject.NestedObjects[0] is InternalTag, "First child of element must be start tag");
}
/// <summary> No tags are missing anywhere within this element (recursive) </summary>
public bool IsProperlyNested {
get { return ((InternalElement)internalObject).IsPropertyNested; }
}
/// <summary>The start or empty-element tag for this element.</summary>
public AXmlTag StartTag {
get { return (AXmlTag)this.Children[0]; }
}
/// <summary>Name with namespace prefix - exactly as in source</summary>
public string Name {
get { return ((InternalTag)internalObject.NestedObjects[0]).Name; }
}
/// <summary>Gets whether an end tag exists for this node.</summary>
public bool HasEndTag {
get { return ((InternalElement)internalObject).HasEndTag; }
}
/// <summary> The end tag, if there is any. Returns null for empty elements "&lt;Element/>" and missing end tags in malformed XML.</summary>
public AXmlTag EndTag {
get {
if (HasEndTag)
return (AXmlTag)this.Children[this.Children.Count - 1];
else
return null;
}
}
/// <summary>
/// Gets the attributes.
/// </summary>
public IEnumerable<AXmlAttribute> Attributes {
get {
return ((AXmlTag)this.Children[0]).Children.OfType<AXmlAttribute>();
}
}
/// <summary>
/// Gets the content (all children except for the start and end tags)
/// </summary>
public IEnumerable<AXmlObject> Content {
get {
int end = this.Children.Count;
if (HasEndTag)
end--;
for (int i = 1; i < end; i++) {
yield return this.Children[i];
}
}
}
/// <summary> The part of name before ":" </summary>
/// <returns> Empty string if not found </returns>
public string Prefix {
get { return ((InternalElement)internalObject).Prefix; }
}
/// <summary> The part of name after ":" </summary>
/// <returns> Empty string if not found </returns>
public string LocalName {
get { return ((InternalElement)internalObject).LocalName; }
}
/// <summary> Resolved namespace of the name </summary>
/// <returns> Empty string if prefix is not found </returns>
public string Namespace {
get {
string prefix = this.Prefix;
return LookupNamespace(prefix);
}
}
/// <summary> Find the default namespace for this context </summary>
[Obsolete("Use LookupNamespace(string.Empty) instead")]
public string FindDefaultNamespace()
{
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 namespaces
if (prefix == "xml") return XmlNamespace;
if (prefix == "xmlns") return XmlnsNamespace;
string lookFor = (prefix.Length > 0 ? "xmlns:" + prefix : "xmlns");
for (AXmlElement current = this; current != null; current = current.Parent as AXmlElement) {
foreach (var attr in current.Attributes) {
if (attr.Name == lookFor)
return attr.Value;
}
}
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; 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) {
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>
/// Get unquoted value of attribute.
/// It looks in the no namespace (empty string).
/// </summary>
/// <returns>Null if not found</returns>
public string GetAttributeValue(string localName)
{
return GetAttributeValue(string.Empty, localName);
}
/// <summary>
/// Get unquoted value of attribute
/// </summary>
/// <param name="namespace">Namespace. Can be no namepace (empty string), which is the default for attributes.</param>
/// <param name="localName">Local name - text after ":"</param>
/// <returns>Null if not found</returns>
public string GetAttributeValue(string @namespace, string localName)
{
@namespace = @namespace ?? string.Empty;
foreach (AXmlAttribute attr in this.Attributes) {
if (attr.LocalName == localName && attr.Namespace == @namespace)
return attr.Value;
}
return null;
}
/// <inheritdoc/>
public override void AcceptVisitor(AXmlVisitor visitor)
{
visitor.VisitElement(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}' Attr:{2} Chld:{3} Nest:{4}]", base.ToString(), this.Name, this.StartTag.Children.Count, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad");
}
}
}

247
ICSharpCode.Decompiler/Xml/AXmlObject.cs

@ -1,247 +0,0 @@ @@ -1,247 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// XML object. Base class for all nodes in the XML document.
/// </summary>
public abstract class AXmlObject : ISegment
{
/// <summary> Empty string. The namespace used if there is no "xmlns" specified </summary>
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";
/// <summary> Namesapce for "xmlns:" prefix: "http://www.w3.org/2000/xmlns/" </summary>
public static readonly string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
readonly AXmlObject parent;
internal readonly int startOffset;
internal readonly InternalObject internalObject;
IList<AXmlObject> children;
internal AXmlObject(AXmlObject parent, int startOffset, InternalObject internalObject)
{
this.parent = parent;
this.startOffset = startOffset;
this.internalObject = internalObject;
}
/// <summary>
/// Creates an XML reader that reads from this document.
/// </summary>
/// <remarks>
/// The reader will ignore comments and processing instructions; and will not have line information.
/// </remarks>
public XmlReader CreateReader()
{
return new AXmlReader(CreateIteratorForReader());
}
/// <summary>
/// Creates an XML reader that reads from this document.
/// </summary>
/// <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(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>
/// Gets the parent node.
/// </summary>
public AXmlObject Parent {
get { return parent; }
}
/// <summary>
/// Gets the list of child objects.
/// </summary>
public IList<AXmlObject> Children {
get {
var result = LazyInit.VolatileRead(ref this.children);
if (result != null) {
return result;
} else {
if (internalObject.NestedObjects != null) {
var array = new AXmlObject[internalObject.NestedObjects.Length];
for (int i = 0; i < array.Length; i++) {
array[i] = internalObject.NestedObjects[i].CreatePublicObject(this, startOffset);
}
result = Array.AsReadOnly(array);
} else {
result = EmptyList<AXmlObject>.Instance;
}
return LazyInit.GetOrSet(ref this.children, result);
}
}
}
/// <summary>
/// Gets a child fully containg the given offset.
/// Goes recursively down the tree.
/// Special case if at the end of attribute or text
/// </summary>
public AXmlObject GetChildAtOffset(int offset)
{
foreach(AXmlObject child in this.Children) {
if (offset == child.EndOffset && (child is AXmlAttribute || child is AXmlText))
return child;
if (child.StartOffset < offset && offset < child.EndOffset) {
return child.GetChildAtOffset(offset);
}
}
return this; // No children at offset
}
/// <summary>
/// The error that occured in the context of this node (excluding nested nodes)
/// </summary>
public IEnumerable<SyntaxError> MySyntaxErrors {
get {
if (internalObject.SyntaxErrors != null) {
return internalObject.SyntaxErrors.Select(e => new SyntaxError(startOffset + e.RelativeStart, startOffset + e.RelativeEnd, e.Description));
} else {
return EmptyList<SyntaxError>.Instance;
}
}
}
/// <summary>
/// The error that occured in the context of this node and all nested nodes.
/// It has O(n) cost.
/// </summary>
public IEnumerable<SyntaxError> SyntaxErrors {
get {
return TreeTraversal.PreOrder(this, n => n.Children).SelectMany(obj => obj.MySyntaxErrors);
}
}
/// <summary> Get all ancestors of this node </summary>
public IEnumerable<AXmlObject> Ancestors {
get {
AXmlObject curr = this.Parent;
while(curr != null) {
yield return curr;
curr = curr.Parent;
}
}
}
#region Helper methods
/// <summary> The part of name before ":" </summary>
/// <returns> Empty string if not found </returns>
internal static string GetNamespacePrefix(string name)
{
if (string.IsNullOrEmpty(name)) return string.Empty;
int colonIndex = name.IndexOf(':');
if (colonIndex != -1) {
return name.Substring(0, colonIndex);
} else {
return string.Empty;
}
}
/// <summary> The part of name after ":" </summary>
/// <returns> Whole name if ":" not found </returns>
internal static string GetLocalName(string name)
{
if (string.IsNullOrEmpty(name)) return string.Empty;
int colonIndex = name.IndexOf(':');
if (colonIndex != -1) {
return name.Remove(0, colonIndex + 1);
} else {
return name ?? string.Empty;
}
}
#endregion
/// <summary> Call appropriate visit method on the given visitor </summary>
public abstract void AcceptVisitor(AXmlVisitor visitor);
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
public int StartOffset {
get { return startOffset; }
}
int ISegment.Offset {
get { return startOffset; }
}
/// <inheritdoc/>
public int Length {
get { return internalObject.Length; }
}
/// <inheritdoc/>
public int EndOffset {
get { return startOffset + internalObject.Length; }
}
}
}

142
ICSharpCode.Decompiler/Xml/AXmlParser.cs

@ -1,142 +0,0 @@ @@ -1,142 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Threading;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// XML parser that is error tolerant.
/// </summary>
public class AXmlParser
{
/// <summary>
/// Generate syntax error when seeing entity reference other then the built-in ones
/// </summary>
public bool UnknownEntityReferenceIsError { get; set; }
IList<AXmlObject> CreatePublic(IList<InternalObject> internalObjects)
{
var publicObjects = new AXmlObject[internalObjects.Count];
int pos = 0;
for (int i = 0; i < internalObjects.Count; i++) {
publicObjects[i] = internalObjects[i].CreatePublicObject(null, pos);
pos += internalObjects[i].Length;
}
return Array.AsReadOnly(publicObjects);
}
/// <summary>
/// Parses a document into a flat list of tags.
/// </summary>
/// <returns>Parsed tag soup.</returns>
public IList<AXmlObject> ParseTagSoup(ITextSource textSource,
CancellationToken cancellationToken = default(CancellationToken))
{
if (textSource == null)
throw new ArgumentNullException("textSource");
var reader = new TagReader(this, textSource, false);
var internalObjects = reader.ReadAllObjects(cancellationToken);
return CreatePublic(internalObjects);
}
/// <summary>
/// Parses a document incrementally into a flat list of tags.
/// </summary>
/// <param name="oldParserState">The parser state from a previous call to ParseIncremental(). Use null for the first call.</param>
/// <param name="newTextSource">The text source for the new document version.</param>
/// <param name="newParserState">Out: the new parser state, pass this to the next ParseIncremental() call.</param>
/// <param name="cancellationToken">Optional: cancellation token.</param>
/// <returns>Parsed tag soup.</returns>
public IList<AXmlObject> ParseTagSoupIncremental(
IncrementalParserState oldParserState, ITextSource newTextSource, out IncrementalParserState newParserState,
CancellationToken cancellationToken = default(CancellationToken))
{
if (newTextSource == null)
throw new ArgumentNullException("newTextSource");
var internalObjects = InternalParseIncremental(oldParserState, newTextSource, out newParserState, false, cancellationToken);
return CreatePublic(internalObjects);
}
List<InternalObject> InternalParseIncremental(
IncrementalParserState oldParserState, ITextSource newTextSource, out IncrementalParserState newParserState,
bool collapseProperlyNestedElements, CancellationToken cancellationToken)
{
var reader = new TagReader(this, newTextSource, collapseProperlyNestedElements);
ITextSourceVersion newVersion = newTextSource.Version;
var reuseMap = oldParserState != null ? oldParserState.GetReuseMapTo(newVersion) : null;
List<InternalObject> internalObjects;
if (reuseMap != null)
internalObjects = reader.ReadAllObjectsIncremental(oldParserState.Objects, reuseMap, cancellationToken);
else
internalObjects = reader.ReadAllObjects(cancellationToken);
if (newVersion != null)
newParserState = new IncrementalParserState(newTextSource.TextLength, newVersion, internalObjects.ToArray());
else
newParserState = null;
return internalObjects;
}
/// <summary>
/// Parses a document.
/// </summary>
public AXmlDocument Parse(ITextSource textSource, CancellationToken cancellationToken = default(CancellationToken))
{
if (textSource == null)
throw new ArgumentNullException("textSource");
var reader = new TagReader(this, textSource, true);
var internalObjects = reader.ReadAllObjects(cancellationToken);
var heuristic = new TagMatchingHeuristics(textSource);
return new AXmlDocument(null, 0, heuristic.CreateDocument(internalObjects, cancellationToken));
}
/// <summary>
/// Parses a document incrementally into a flat list of tags.
/// </summary>
/// <param name="oldParserState">The parser state from a previous call to ParseIncremental(). Use null for the first call.</param>
/// <param name="newTextSource">The text source for the new document version.</param>
/// <param name="newParserState">Out: the new parser state, pass this to the next ParseIncremental() call.</param>
/// <param name="cancellationToken">Optional: cancellation token.</param>
/// <returns>Parsed tag soup.</returns>
public AXmlDocument ParseIncremental(
IncrementalParserState oldParserState, ITextSource newTextSource, out IncrementalParserState newParserState,
CancellationToken cancellationToken = default(CancellationToken))
{
if (newTextSource == null)
throw new ArgumentNullException("newTextSource");
var internalObjects = InternalParseIncremental(oldParserState, newTextSource, out newParserState, true, cancellationToken);
var heuristic = new TagMatchingHeuristics(newTextSource);
return new AXmlDocument(null, 0, heuristic.CreateDocument(internalObjects, cancellationToken));
}
/// <summary>
/// Checks whether the given name is a valid XML name.
/// </summary>
public static bool IsValidXmlName(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("The XML name cannot be null, empty or consist solely of white space", "name");
return TagReader.IsValidName(name);
}
}
}

477
ICSharpCode.Decompiler/Xml/AXmlReader.cs

@ -1,477 +0,0 @@ @@ -1,477 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// XmlReader implementation that reads from an <see cref="AXmlDocument"/>.
/// </summary>
sealed class AXmlReader : XmlReader, IXmlLineInfo
{
readonly ObjectIterator objectIterator;
readonly XmlReaderSettings settings;
Func<int, TextLocation> offsetToTextLocation;
readonly XmlNameTable nameTable;
readonly XmlNamespaceManager nsManager;
ReadState readState = ReadState.Initial;
XmlNodeType elementNodeType = XmlNodeType.None;
IList<InternalAttribute> attributes;
int attributeIndex = -1;
bool inAttributeValue;
internal AXmlReader(ObjectIterator objectIterator, XmlReaderSettings settings = null, Func<int, TextLocation> offsetToTextLocation = null)
{
this.objectIterator = objectIterator;
this.settings = settings ?? new XmlReaderSettings();
this.offsetToTextLocation = offsetToTextLocation;
this.nameTable = this.settings.NameTable ?? new NameTable();
this.nsManager = new XmlNamespaceManager(this.nameTable);
objectIterator.StopAtElementEnd = true;
}
public override void ResolveEntity()
{
throw new NotSupportedException();
}
public override ReadState ReadState {
get { return readState; }
}
public override XmlReaderSettings Settings {
get { return settings; }
}
public override bool ReadAttributeValue()
{
if (attributeIndex >= 0 && !inAttributeValue) {
inAttributeValue = true;
return true;
}
return false;
}
public override bool Read()
{
switch (readState) {
case ReadState.Initial:
readState = ReadState.Interactive;
return ReadCurrentPosition();
case ReadState.Interactive:
LeaveNode();
objectIterator.MoveInto();
return ReadCurrentPosition();
default:
return false;
}
}
bool ReadCurrentPosition()
{
attributes = null;
attributeIndex = -1;
inAttributeValue = false;
while (true) {
var obj = objectIterator.CurrentObject;
if (obj == null) {
readState = ReadState.EndOfFile;
elementNodeType = XmlNodeType.None;
return false;
} else if (objectIterator.IsAtElementEnd) {
if (IsEmptyElement) {
// Don't report EndElement for empty elements
nsManager.PopScope();
} else {
elementNodeType = XmlNodeType.EndElement;
return true;
}
} else if (obj is InternalElement) {
// element start
elementNodeType = XmlNodeType.Element;
InternalTag startTag = ((InternalTag)obj.NestedObjects[0]);
nsManager.PushScope();
if (startTag.NestedObjects != null) {
attributes = startTag.NestedObjects.OfType<InternalAttribute>().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;
if (text.ContainsOnlyWhitespace) {
elementNodeType = XmlNodeType.Whitespace;
} else {
elementNodeType = XmlNodeType.Text;
}
return true;
} else if (obj is InternalTag) {
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 if (tag.IsProcessingInstruction && !settings.IgnoreProcessingInstructions) {
if (tag.Name == "xml") {
elementNodeType = XmlNodeType.XmlDeclaration;
attributes = tag.NestedObjects.OfType<InternalAttribute>().ToList();
} else {
elementNodeType = XmlNodeType.ProcessingInstruction;
}
return true;
} else if (tag.IsCData) {
elementNodeType = XmlNodeType.CDATA;
return true;
} else {
// TODO all other tags
}
} else {
throw new NotSupportedException();
}
objectIterator.MoveInto();
}
}
void LeaveNode()
{
if (elementNodeType == XmlNodeType.EndElement) {
nsManager.PopScope();
}
}
public override void Skip()
{
if (readState == ReadState.Interactive) {
MoveToElement();
LeaveNode();
objectIterator.MoveNext();
ReadCurrentPosition();
}
}
public override string Prefix {
get {
if (readState != ReadState.Interactive)
return string.Empty;
if (attributeIndex >= 0) {
if (inAttributeValue)
return string.Empty;
return nameTable.Add(AXmlObject.GetNamespacePrefix(attributes[attributeIndex].Name));
}
InternalElement element = objectIterator.CurrentObject as InternalElement;
return element != null ? nameTable.Add(element.Prefix) : string.Empty;
}
}
public override string NamespaceURI {
get {
if (readState != ReadState.Interactive)
return string.Empty;
if (attributeIndex >= 0 && !inAttributeValue && attributes[attributeIndex].Name == "xmlns")
return AXmlObject.XmlnsNamespace;
return LookupNamespace(this.Prefix) ?? string.Empty;
}
}
public override string LocalName {
get {
if (readState != ReadState.Interactive)
return string.Empty;
if (attributeIndex >= 0) {
if (inAttributeValue)
return string.Empty;
return nameTable.Add(AXmlObject.GetLocalName(attributes[attributeIndex].Name));
}
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 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;
}
}
public override string Value {
get {
if (readState != ReadState.Interactive)
return string.Empty;
if (attributeIndex >= 0)
return attributes[attributeIndex].Value;
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<InternalAttribute>()) {
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 {
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;
}
}
}
public override XmlNodeType NodeType {
get {
if (attributeIndex >= 0)
return inAttributeValue ? XmlNodeType.Text : XmlNodeType.Attribute;
else
return elementNodeType;
}
}
public override XmlNameTable NameTable {
get { return nameTable; }
}
public override bool MoveToFirstAttribute()
{
return DoMoveToAttribute(0);
}
public override bool MoveToNextAttribute()
{
return DoMoveToAttribute(attributeIndex + 1);
}
public override void MoveToAttribute(int i)
{
if (!DoMoveToAttribute(i))
throw new ArgumentOutOfRangeException("i");
}
bool DoMoveToAttribute(int i)
{
if (i >= 0 && i < this.AttributeCount) {
attributeIndex = i;
inAttributeValue = false;
return true;
}
return false;
}
public override bool MoveToElement()
{
if (attributeIndex >= 0) {
attributeIndex = -1;
inAttributeValue = false;
return true;
}
return false;
}
int GetAttributeIndex(string name)
{
if (attributes == null)
return -1;
for (int i = 0; i < attributes.Count; i++) {
if (attributes[i].Name == name)
return i;
}
return -1;
}
int GetAttributeIndex(string name, string ns)
{
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)) ?? string.Empty) == ns)
return i;
}
return -1;
}
public override bool MoveToAttribute(string name, string ns)
{
return DoMoveToAttribute(GetAttributeIndex(name, ns));
}
public override bool MoveToAttribute(string name)
{
return DoMoveToAttribute(GetAttributeIndex(name));
}
public override string LookupNamespace(string prefix)
{
return nsManager.LookupNamespace(prefix);
}
public override string GetAttribute(int i)
{
if (attributes == null || i < 0 || i >= attributes.Count)
return null;
return attributes[i].Value;
}
public override string GetAttribute(string name, string namespaceURI)
{
return GetAttribute(GetAttributeIndex(name, namespaceURI));
}
public override string GetAttribute(string name)
{
return GetAttribute(GetAttributeIndex(name));
}
public override bool EOF {
get { return readState == ReadState.EndOfFile; }
}
public override int Depth {
get {
if (attributeIndex < 0)
return objectIterator.Depth;
else
return objectIterator.Depth + (inAttributeValue ? 2 : 1);
}
}
public override void Close()
{
readState = ReadState.Closed;
offsetToTextLocation = null;
}
public override string BaseURI {
get { return string.Empty; }
}
public override int AttributeCount {
get { return attributes != null ? attributes.Count : 0; }
}
int CurrentPosition {
get {
if (attributeIndex < 0)
return objectIterator.CurrentPosition;
else
return objectIterator.CurrentPosition + attributes[attributeIndex].StartRelativeToParent;
}
}
public int LineNumber {
get {
if (offsetToTextLocation != null)
return offsetToTextLocation(CurrentPosition).Line;
else
return 0;
}
}
public int LinePosition {
get {
if (offsetToTextLocation != null)
return offsetToTextLocation(CurrentPosition).Column - 1;
else
return 0;
}
}
bool IXmlLineInfo.HasLineInfo()
{
return offsetToTextLocation != null;
}
}
}

96
ICSharpCode.Decompiler/Xml/AXmlTag.cs

@ -1,96 +0,0 @@ @@ -1,96 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.ObjectModel;
using System.Globalization;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Represents any markup starting with "&lt;" and (hopefully) ending with ">"
/// </summary>
public class AXmlTag : AXmlObject
{
/// <summary> These identify the start of DTD elements </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification="ReadOnlyCollection is immutable")]
public static readonly ReadOnlyCollection<string> DtdNames = new ReadOnlyCollection<string>(
new string[] {"<!DOCTYPE", "<!NOTATION", "<!ELEMENT", "<!ATTLIST", "<!ENTITY" } );
new readonly InternalTag internalObject;
internal AXmlTag(AXmlObject parent, int startOffset, InternalTag internalObject)
: base(parent, startOffset, internalObject)
{
this.internalObject = internalObject;
}
/// <summary> Opening bracket - usually "&lt;" </summary>
public string OpeningBracket {
get { return internalObject.OpeningBracket; }
}
/// <summary> Name following the opening bracket </summary>
public string Name {
get { return internalObject.Name; }
}
/// <summary> Gets the segment containing the tag name </summary>
public ISegment NameSegment {
get {
int start = startOffset + internalObject.RelativeNameStart;
return new XmlSegment(start, start + internalObject.Name.Length);
}
}
/// <summary> Closing bracket - usually "&gt;" </summary>
public string ClosingBracket {
get { return internalObject.ClosingBracket; }
}
/// <summary> True if tag starts with "&lt;" </summary>
public bool IsStartOrEmptyTag { get { return internalObject.IsStartOrEmptyTag; } }
/// <summary> True if tag starts with "&lt;" and ends with "&gt;" </summary>
public bool IsStartTag { get { return internalObject.IsStartTag; } }
/// <summary> True if tag starts with "&lt;" and does not end with "&gt;" </summary>
public bool IsEmptyTag { get { return internalObject.IsEmptyTag; } }
/// <summary> True if tag starts with "&lt;/" </summary>
public bool IsEndTag { get { return internalObject.IsEndTag; } }
/// <summary> True if tag starts with "&lt;?" </summary>
public bool IsProcessingInstruction { get { return internalObject.IsProcessingInstruction; } }
/// <summary> True if tag starts with "&lt;!--" </summary>
public bool IsComment { get { return internalObject.IsComment; } }
/// <summary> True if tag starts with "&lt;![CDATA[" </summary>
public bool IsCData { get { return internalObject.IsCData; } }
/// <summary> True if tag starts with one of the DTD starts </summary>
public bool IsDocumentType { get { return internalObject.IsDocumentType; } }
/// <summary> True if tag starts with "&lt;!" </summary>
public bool IsUnknownBang { get { return internalObject.IsUnknownBang; } }
/// <inheritdoc/>
public override void AcceptVisitor(AXmlVisitor visitor)
{
visitor.VisitTag(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count);
}
}
}

63
ICSharpCode.Decompiler/Xml/AXmlText.cs

@ -1,63 +0,0 @@ @@ -1,63 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Globalization;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Whitespace or character data
/// </summary>
public class AXmlText : AXmlObject
{
internal AXmlText(AXmlObject parent, int startOffset, InternalText internalObject)
: base(parent, startOffset, internalObject)
{
}
// /// <summary> The type of the text node </summary>
// public TextType Type {
// get { return ((InternalText)internalObject).Type; }
// }
/// <summary> The text with all entity references resloved </summary>
public string Value {
get { return ((InternalText)internalObject).Value; }
}
/// <summary> True if the text contains only whitespace characters </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
Justification = "System.Xml also uses 'Whitespace'")]
public bool ContainsOnlyWhitespace {
get { return ((InternalText)internalObject).ContainsOnlyWhitespace; }
}
/// <inheritdoc/>
public override void AcceptVisitor(AXmlVisitor visitor)
{
visitor.VisitText(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} Text.Length={1}]", base.ToString(), this.Value.Length);
}
}
}

62
ICSharpCode.Decompiler/Xml/AXmlVisitor.cs

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Text;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Derive from this class to create visitor for the XML tree
/// </summary>
public abstract class AXmlVisitor
{
/// <summary> Visit AXmlDocument </summary>
public virtual void VisitDocument(AXmlDocument document)
{
foreach (AXmlObject child in document.Children)
child.AcceptVisitor(this);
}
/// <summary> Visit AXmlElement </summary>
public virtual void VisitElement(AXmlElement element)
{
foreach (AXmlObject child in element.Children)
child.AcceptVisitor(this);
}
/// <summary> Visit AXmlTag </summary>
public virtual void VisitTag(AXmlTag tag)
{
foreach (AXmlObject child in tag.Children)
child.AcceptVisitor(this);
}
/// <summary> Visit AXmlAttribute </summary>
public virtual void VisitAttribute(AXmlAttribute attribute)
{
}
/// <summary> Visit AXmlText </summary>
public virtual void VisitText(AXmlText text)
{
}
}
}

43
ICSharpCode.Decompiler/Xml/AnchorMovementType.cs

@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Defines how a text anchor moves.
/// </summary>
public enum AnchorMovementType
{
/// <summary>
/// When text is inserted at the anchor position, the type of the insertion
/// determines where the caret moves to. For normal insertions, the anchor will move
/// after the inserted text.
/// </summary>
Default,
/// <summary>
/// Behaves like a start marker - when text is inserted at the anchor position, the anchor will stay
/// before the inserted text.
/// </summary>
BeforeInsertion,
/// <summary>
/// Behave like an end marker - when text is insered at the anchor position, the anchor will move
/// after the inserted text.
/// </summary>
AfterInsertion
}
}

69
ICSharpCode.Decompiler/Xml/ISegment.cs

@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// An (Offset,Length)-pair.
/// </summary>
public interface ISegment
{
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
int Offset { get; }
/// <summary>
/// Gets the length of the segment.
/// </summary>
/// <remarks>For line segments (IDocumentLine), the length does not include the line delimeter.</remarks>
int Length { get; }
/// <summary>
/// Gets the end offset of the segment.
/// </summary>
/// <remarks>EndOffset = Offset + Length;</remarks>
int EndOffset { get; }
}
/// <summary>
/// Extension methods for <see cref="ISegment"/>.
/// </summary>
public static class ISegmentExtensions
{
/// <summary>
/// Gets whether <paramref name="segment"/> fully contains the specified segment.
/// </summary>
/// <remarks>
/// Use <c>segment.Contains(offset, 0)</c> to detect whether a segment (end inclusive) contains offset;
/// use <c>segment.Contains(offset, 1)</c> to detect whether a segment (end exclusive) contains offset.
/// </remarks>
public static bool Contains(this ISegment segment, int offset, int length)
{
return segment.Offset <= offset && offset + length <= segment.EndOffset;
}
/// <summary>
/// Gets whether <paramref name="thisSegment"/> fully contains the specified segment.
/// </summary>
public static bool Contains(this ISegment thisSegment, ISegment segment)
{
return segment != null && thisSegment.Offset <= segment.Offset && segment.EndOffset <= thisSegment.EndOffset;
}
}
}

218
ICSharpCode.Decompiler/Xml/ITextSource.cs

@ -1,218 +0,0 @@ @@ -1,218 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// A read-only view on a (potentially mutable) text source.
/// The IDocument interface derives from this interface.
/// </summary>
public interface ITextSource
{
/// <summary>
/// Gets a version identifier for this text source.
/// Returns null for unversioned text sources.
/// </summary>
ITextSourceVersion Version { get; }
/// <summary>
/// Creates an immutable snapshot of this text source.
/// Unlike all other methods in this interface, this method is thread-safe.
/// </summary>
ITextSource CreateSnapshot();
/// <summary>
/// Creates an immutable snapshot of a part of this text source.
/// Unlike all other methods in this interface, this method is thread-safe.
/// </summary>
ITextSource CreateSnapshot(int offset, int length);
/// <summary>
/// Creates a new TextReader to read from this text source.
/// </summary>
TextReader CreateReader();
/// <summary>
/// Creates a new TextReader to read from this text source.
/// </summary>
TextReader CreateReader(int offset, int length);
/// <summary>
/// Gets the total text length.
/// </summary>
/// <returns>The length of the text, in characters.</returns>
/// <remarks>This is the same as Text.Length, but is more efficient because
/// it doesn't require creating a String object.</remarks>
int TextLength { get; }
/// <summary>
/// Gets the whole text as string.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
string Text { get; }
/// <summary>
/// Gets a character at the specified position in the document.
/// </summary>
/// <paramref name="offset">The index of the character to get.</paramref>
/// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception>
/// <returns>The character at the specified position.</returns>
/// <remarks>This is the same as Text[offset], but is more efficient because
/// it doesn't require creating a String object.</remarks>
char GetCharAt(int offset);
/// <summary>
/// Retrieves the text for a portion of the document.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
/// <remarks>This is the same as Text.Substring, but is more efficient because
/// it doesn't require creating a String object for the whole document.</remarks>
string GetText(int offset, int length);
/// <summary>
/// Retrieves the text for a portion of the document.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
string GetText(ISegment segment);
/// <summary>
/// Writes the text from this document into the TextWriter.
/// </summary>
void WriteTextTo(TextWriter writer);
/// <summary>
/// Writes the text from this document into the TextWriter.
/// </summary>
void WriteTextTo(TextWriter writer, int offset, int length);
/// <summary>
/// Gets the index of the first occurrence of the character in the specified array.
/// </summary>
/// <param name="c">Character to search for</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The first index where the character was found; or -1 if no occurrence was found.</returns>
int IndexOf(char c, int startIndex, int count);
/// <summary>
/// Gets the index of the first occurrence of any character in the specified array.
/// </summary>
/// <param name="anyOf">Characters to search for</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
int IndexOfAny(char[] anyOf, int startIndex, int count);
/// <summary>
/// Gets the index of the first occurrence of the specified search text in this text source.
/// </summary>
/// <param name="searchText">The search text</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <param name="comparisonType">String comparison to use.</param>
/// <returns>The first index where the search term was found; or -1 if no occurrence was found.</returns>
int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType);
/// <summary>
/// Gets the index of the last occurrence of the specified character in this text source.
/// </summary>
/// <param name="c">The search character</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
int LastIndexOf(char c, int startIndex, int count);
/// <summary>
/// Gets the index of the last occurrence of the specified search text in this text source.
/// </summary>
/// <param name="searchText">The search text</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <param name="comparisonType">String comparison to use.</param>
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType);
/* What about:
void Insert (int offset, string value);
void Remove (int offset, int count);
void Remove (ISegment segment);
void Replace (int offset, int count, string value);
Or more search operations:
IEnumerable<int> SearchForward (string pattern, int startIndex);
IEnumerable<int> SearchForwardIgnoreCase (string pattern, int startIndex);
IEnumerable<int> SearchBackward (string pattern, int startIndex);
IEnumerable<int> SearchBackwardIgnoreCase (string pattern, int startIndex);
*/
}
/// <summary>
/// Represents a version identifier for a text source.
/// </summary>
/// <remarks>
/// Verions can be used to efficiently detect whether a document has changed and needs reparsing;
/// or even to implement incremental parsers.
/// It is a separate class from ITextSource to allow the GC to collect the text source while
/// the version checkpoint is still in use.
/// </remarks>
public interface ITextSourceVersion
{
/// <summary>
/// Gets whether this checkpoint belongs to the same document as the other checkpoint.
/// </summary>
/// <remarks>
/// Returns false when given <c>null</c>.
/// </remarks>
bool BelongsToSameDocumentAs(ITextSourceVersion other);
/// <summary>
/// Compares the age of this checkpoint to the other checkpoint.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this version.</exception>
/// <returns>-1 if this version is older than <paramref name="other"/>.
/// 0 if <c>this</c> version instance represents the same version as <paramref name="other"/>.
/// 1 if this version is newer than <paramref name="other"/>.</returns>
int CompareAge(ITextSourceVersion other);
/// <summary>
/// Gets the changes from this checkpoint to the other checkpoint.
/// If 'other' is older than this checkpoint, reverse changes are calculated.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
IEnumerable<TextChangeEventArgs> GetChangesTo(ITextSourceVersion other);
/// <summary>
/// Calculates where the offset has moved in the other buffer version.
/// </summary>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
int MoveOffsetTo(ITextSourceVersion other, int oldOffset, AnchorMovementType movement = AnchorMovementType.Default);
}
}

114
ICSharpCode.Decompiler/Xml/IncrementalParserState.cs

@ -1,114 +0,0 @@ @@ -1,114 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Encapsulates the state of the incremental tag soup parser.
/// </summary>
public class IncrementalParserState
{
internal readonly int TextLength;
internal readonly ITextSourceVersion Version;
internal readonly InternalObject[] Objects;
internal IncrementalParserState(int textLength, ITextSourceVersion version, InternalObject[] objects)
{
this.TextLength = textLength;
this.Version = version;
this.Objects = objects;
}
internal List<UnchangedSegment> GetReuseMapTo(ITextSourceVersion newVersion)
{
ITextSourceVersion oldVersion = this.Version;
if (oldVersion == null || newVersion == null)
return null;
if (!oldVersion.BelongsToSameDocumentAs(newVersion))
return null;
List<UnchangedSegment> reuseMap = new List<UnchangedSegment>();
reuseMap.Add(new UnchangedSegment(0, 0, this.TextLength));
foreach (var change in oldVersion.GetChangesTo(newVersion)) {
bool needsSegmentRemoval = false;
for (int i = 0; i < reuseMap.Count; i++) {
UnchangedSegment segment = reuseMap[i];
if (segment.NewOffset + segment.Length <= change.Offset) {
// change is completely after this segment
continue;
}
if (change.Offset + change.RemovalLength <= segment.NewOffset) {
// change is completely before this segment
segment.NewOffset += change.InsertionLength - change.RemovalLength;
reuseMap[i] = segment;
continue;
}
// Change is overlapping segment.
// Split segment into two parts: the part before change, and the part after change.
var segmentBefore = new UnchangedSegment(segment.OldOffset, segment.NewOffset, change.Offset - segment.NewOffset);
Debug.Assert(segmentBefore.Length < segment.Length);
int lengthAtEnd = segment.NewOffset + segment.Length - (change.Offset + change.RemovalLength);
var segmentAfter = new UnchangedSegment(
segment.OldOffset + segment.Length - lengthAtEnd,
change.Offset + change.InsertionLength,
lengthAtEnd);
Debug.Assert(segmentAfter.Length < segment.Length);
Debug.Assert(segmentBefore.Length + segmentAfter.Length <= segment.Length);
Debug.Assert(segmentBefore.NewOffset + segmentBefore.Length <= segmentAfter.NewOffset);
Debug.Assert(segmentBefore.OldOffset + segmentBefore.Length <= segmentAfter.OldOffset);
if (segmentBefore.Length > 0 && segmentAfter.Length > 0) {
reuseMap[i] = segmentBefore;
reuseMap.Insert(++i, segmentAfter);
} else if (segmentBefore.Length > 0) {
reuseMap[i] = segmentBefore;
} else {
reuseMap[i] = segmentAfter;
if (segmentAfter.Length <= 0)
needsSegmentRemoval = true;
}
}
if (needsSegmentRemoval)
reuseMap.RemoveAll(s => s.Length <= 0);
}
return reuseMap;
}
}
struct UnchangedSegment
{
public int OldOffset;
public int NewOffset;
public int Length;
public UnchangedSegment(int oldOffset, int newOffset, int length)
{
this.OldOffset = oldOffset;
this.NewOffset = newOffset;
this.Length = length;
}
public override string ToString()
{
return string.Format("[UnchangedSegment OldOffset={0}, NewOffset={1}, Length={2}]", OldOffset, NewOffset, Length);
}
}
}

175
ICSharpCode.Decompiler/Xml/InternalDocument.cs

@ -1,175 +0,0 @@ @@ -1,175 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Xml
{
internal abstract class InternalObject
{
public int StartRelativeToParent;
public int Length;
/// <summary>Length that was touched to parsed this object.</summary>
public int LengthTouched;
public InternalSyntaxError[] SyntaxErrors;
public InternalObject[] NestedObjects;
public InternalObject SetStartRelativeToParent(int newStartRelativeToParent)
{
if (newStartRelativeToParent == StartRelativeToParent)
return this;
InternalObject obj = (InternalObject)MemberwiseClone();
obj.StartRelativeToParent = newStartRelativeToParent;
return obj;
}
public abstract AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset);
}
sealed class InternalDocument : InternalObject
{
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset)
{
return new AXmlDocument(parent, (parent != null ? parentStartOffset + StartRelativeToParent : parentStartOffset), this);
}
}
sealed class InternalText : InternalObject
{
public TextType Type;
public bool ContainsOnlyWhitespace;
public string Value;
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset)
{
return new AXmlText(parent, (parent != null ? parentStartOffset + StartRelativeToParent : parentStartOffset), this);
}
public override string ToString()
{
return "Text: " + this.Value.Replace("\n", "\\n").Replace("\r", "\\r");
}
}
sealed class InternalTag : InternalObject
{
public string OpeningBracket;
public int RelativeNameStart;
public string Name;
public string ClosingBracket;
/// <summary> True if tag starts with "&lt;" </summary>
public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } }
/// <summary> True if tag starts with "&lt;" and ends with "&gt;" </summary>
public bool IsStartTag { get { return OpeningBracket == "<" && ClosingBracket == ">"; } }
/// <summary> True if tag starts with "&lt;" and does not end with "&gt;" </summary>
public bool IsEmptyTag { get { return OpeningBracket == "<" && ClosingBracket != ">" ; } }
/// <summary> True if tag starts with "&lt;/" </summary>
public bool IsEndTag { get { return OpeningBracket == "</"; } }
/// <summary> True if tag starts with "&lt;?" </summary>
public bool IsProcessingInstruction { get { return OpeningBracket == "<?"; } }
/// <summary> True if tag starts with "&lt;!--" </summary>
public bool IsComment { get { return OpeningBracket == "<!--"; } }
/// <summary> True if tag starts with "&lt;![CDATA[" </summary>
public bool IsCData { get { return OpeningBracket == "<![CDATA["; } }
/// <summary> True if tag starts with one of the DTD starts </summary>
public bool IsDocumentType { get { return AXmlTag.DtdNames.Contains(OpeningBracket); } }
/// <summary> True if tag starts with "&lt;!" </summary>
public bool IsUnknownBang { get { return OpeningBracket == "<!"; } }
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset)
{
return new AXmlTag(parent, (parent != null ? parentStartOffset + StartRelativeToParent : parentStartOffset), this);
}
public override string ToString()
{
return "Tag: " + OpeningBracket + Name + ClosingBracket;
}
public InternalTag AddSyntaxError(string description)
{
if (this.SyntaxErrors != null && this.SyntaxErrors.Length > 0)
return this; // don't add error if there already is one
InternalTag tag = (InternalTag)MemberwiseClone();
tag.SyntaxErrors = new InternalSyntaxError[] { new InternalSyntaxError(0, Length, description) };
return tag;
}
}
struct InternalSyntaxError
{
public readonly int RelativeStart;
public readonly int RelativeEnd;
public readonly string Description;
public InternalSyntaxError(int relativeStart, int relativeEnd, string description)
{
this.RelativeStart = relativeStart;
this.RelativeEnd = relativeEnd;
this.Description = description;
}
}
sealed class InternalAttribute : InternalObject
{
public string Name;
public int EqualsSignLength; // length of equals sign including the surrounding whitespace
public string Value;
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset)
{
return new AXmlAttribute(parent, (parent != null ? parentStartOffset + StartRelativeToParent : parentStartOffset), this);
}
public override string ToString()
{
return "Attribute: " + Name + "='" + Value + "'";
}
}
sealed class InternalElement : InternalObject
{
public bool HasEndTag;
public bool IsPropertyNested;
public readonly string Name;
public InternalElement(InternalTag tag)
{
this.Name = tag.Name;
}
public string Prefix {
get { return AXmlObject.GetNamespacePrefix(Name); }
}
public string LocalName {
get { return AXmlObject.GetLocalName(Name); }
}
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset)
{
return new AXmlElement(parent, (parent != null ? parentStartOffset + StartRelativeToParent : parentStartOffset), this);
}
public override string ToString()
{
return "Element: " + NestedObjects[0].ToString();
}
}
}

88
ICSharpCode.Decompiler/Xml/Log.cs

@ -1,88 +0,0 @@ @@ -1,88 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
namespace ICSharpCode.Decompiler.Xml
{
static class Log
{
/// <summary> Throws exception if condition is false </summary>
internal static void Assert(bool condition, string message)
{
if (!condition) {
throw new InternalException("Assertion failed: " + message);
}
}
/// <summary> Throws exception if condition is false </summary>
[Conditional("DEBUG")]
internal static void DebugAssert(bool condition, string message)
{
if (!condition) {
throw new InternalException("Assertion failed: " + message);
}
}
[Conditional("DEBUG")]
internal static void WriteLine(string text, params object[] pars)
{
//System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "XML: " + text, pars));
}
}
/// <summary>
/// Exception used for internal errors in XML parser.
/// This exception indicates a bug in NRefactory.
/// </summary>
[Serializable]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is not public because it is not supposed to be caught by user code - it indicates a bug in AvalonEdit.")]
class InternalException : Exception
{
/// <summary>
/// Creates a new InternalException instance.
/// </summary>
public InternalException() : base()
{
}
/// <summary>
/// Creates a new InternalException instance.
/// </summary>
public InternalException(string message) : base(message)
{
}
/// <summary>
/// Creates a new InternalException instance.
/// </summary>
public InternalException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Creates a new InternalException instance.
/// </summary>
protected InternalException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

109
ICSharpCode.Decompiler/Xml/ObjectIterator.cs

@ -1,109 +0,0 @@ @@ -1,109 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Iterates through an internal object tree.
/// </summary>
sealed class ObjectIterator
{
Stack<InternalObject[]> listStack = new Stack<InternalObject[]>();
Stack<int> indexStack = new Stack<int>();
InternalObject[] objects;
int currentIndex;
InternalObject currentObject;
int currentPosition;
internal bool StopAtElementEnd;
bool isAtElementEnd;
public ObjectIterator(InternalObject[] objects, int startPosition = 0)
{
this.currentPosition = startPosition;
this.objects = objects;
if (objects.Length > 0)
this.currentObject = objects[0];
}
public InternalObject CurrentObject {
get { return currentObject; }
}
public int CurrentPosition {
get { return currentPosition; }
}
public bool IsAtElementEnd {
get { return isAtElementEnd; }
}
public int Depth {
get { return listStack.Count; }
}
public void MoveNext()
{
if (currentObject == null)
return;
currentIndex++;
currentPosition += currentObject.Length;
isAtElementEnd = false;
while (currentIndex >= objects.Length && listStack.Count > 0) {
objects = listStack.Pop();
currentIndex = indexStack.Pop();
if (this.StopAtElementEnd) {
isAtElementEnd = true;
break;
} else {
currentIndex++;
}
}
currentObject = (currentIndex < objects.Length ? objects[currentIndex] : null);
}
public void MoveInto()
{
if (isAtElementEnd || !(currentObject is InternalElement)) {
MoveNext();
} else {
listStack.Push(objects);
indexStack.Push(currentIndex);
objects = currentObject.NestedObjects;
currentIndex = 0;
currentObject = objects[0];
}
}
/// <summary>
/// Skips all nodes in front of 'position'
/// </summary>
public void SkipTo(int position)
{
while (currentObject != null && currentPosition < position) {
if (currentPosition + currentObject.Length <= position)
MoveNext();
else
MoveInto();
}
}
}
}

52
ICSharpCode.Decompiler/Xml/ReuseEqualityComparer.cs

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Determines whether two objects are identical (one is a reused version of the other).
/// </summary>
public class ReuseEqualityComparer : IEqualityComparer<AXmlObject>
{
/// <summary>
/// Determines whether two objects are identical (one is a reused version of the other).
/// </summary>
public bool Equals(AXmlObject x, AXmlObject y)
{
if (x == y)
return true;
if (x == null || y == null)
return false;
return x.internalObject == y.internalObject;
}
/// <summary>
/// Gets the object's hash code so that reused versions of an object have the same hash code.
/// </summary>
public int GetHashCode(AXmlObject obj)
{
if (obj == null)
return 0;
else
return obj.internalObject.GetHashCode();
}
}
}

160
ICSharpCode.Decompiler/Xml/StringTextSource.cs

@ -1,160 +0,0 @@ @@ -1,160 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Implements the ITextSource interface using a string.
/// </summary>
[Serializable]
public class StringTextSource : ITextSource
{
/// <summary>
/// Gets a text source containing the empty string.
/// </summary>
public static readonly StringTextSource Empty = new StringTextSource(string.Empty);
readonly string text;
readonly ITextSourceVersion version;
/// <summary>
/// Creates a new StringTextSource with the given text.
/// </summary>
public StringTextSource(string text)
{
if (text == null)
throw new ArgumentNullException("text");
this.text = text;
}
/// <summary>
/// Creates a new StringTextSource with the given text.
/// </summary>
public StringTextSource(string text, ITextSourceVersion version)
{
if (text == null)
throw new ArgumentNullException("text");
this.text = text;
this.version = version;
}
/// <inheritdoc/>
public ITextSourceVersion Version {
get { return version; }
}
/// <inheritdoc/>
public int TextLength {
get { return text.Length; }
}
/// <inheritdoc/>
public string Text {
get { return text; }
}
/// <inheritdoc/>
public ITextSource CreateSnapshot()
{
return this; // StringTextSource is immutable
}
/// <inheritdoc/>
public ITextSource CreateSnapshot(int offset, int length)
{
return new StringTextSource(text.Substring(offset, length));
}
/// <inheritdoc/>
public TextReader CreateReader()
{
return new StringReader(text);
}
/// <inheritdoc/>
public TextReader CreateReader(int offset, int length)
{
return new StringReader(text.Substring(offset, length));
}
/// <inheritdoc/>
public void WriteTextTo(TextWriter writer)
{
writer.Write(text);
}
/// <inheritdoc/>
public void WriteTextTo(TextWriter writer, int offset, int length)
{
writer.Write(text.Substring(offset, length));
}
/// <inheritdoc/>
public char GetCharAt(int offset)
{
return text[offset];
}
/// <inheritdoc/>
public string GetText(int offset, int length)
{
return text.Substring(offset, length);
}
/// <inheritdoc/>
public string GetText(ISegment segment)
{
if (segment == null)
throw new ArgumentNullException("segment");
return text.Substring(segment.Offset, segment.Length);
}
/// <inheritdoc/>
public int IndexOf(char c, int startIndex, int count)
{
return text.IndexOf(c, startIndex, count);
}
/// <inheritdoc/>
public int IndexOfAny(char[] anyOf, int startIndex, int count)
{
return text.IndexOfAny(anyOf, startIndex, count);
}
/// <inheritdoc/>
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
return text.IndexOf(searchText, startIndex, count, comparisonType);
}
/// <inheritdoc/>
public int LastIndexOf(char c, int startIndex, int count)
{
return text.LastIndexOf(c, startIndex + count - 1, count);
}
/// <inheritdoc/>
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
return text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType);
}
}
}

72
ICSharpCode.Decompiler/Xml/SyntaxError.cs

@ -1,72 +0,0 @@ @@ -1,72 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// A syntax error.
/// </summary>
public class SyntaxError : ISegment
{
readonly int startOffset;
readonly int endOffset;
readonly string description;
/// <summary>
/// Creates a new syntax error.
/// </summary>
public SyntaxError(int startOffset, int endOffset, string description)
{
if (description == null)
throw new ArgumentNullException("description");
this.startOffset = startOffset;
this.endOffset = endOffset;
this.description = description;
}
/// <summary>
/// Gets a description of the syntax error.
/// </summary>
public string Description {
get { return description; }
}
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
public int StartOffset {
get { return startOffset; }
}
int ISegment.Offset {
get { return startOffset; }
}
/// <inheritdoc/>
public int Length {
get { return endOffset - startOffset; }
}
/// <inheritdoc/>
public int EndOffset {
get { return endOffset; }
}
}
}

351
ICSharpCode.Decompiler/Xml/TagMatchingHeuristics.cs

@ -1,351 +0,0 @@ @@ -1,351 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Collections.Immutable;
namespace ICSharpCode.Decompiler.Xml
{
class TagMatchingHeuristics
{
readonly ITextSource textSource;
const int MaxConfigurationCount = 30;
public TagMatchingHeuristics(ITextSource textSource)
{
this.textSource = textSource;
}
public InternalDocument CreateDocument(List<InternalObject> tagSoup, CancellationToken cancellationToken)
{
var stack = InsertPlaceholderTags(tagSoup, cancellationToken);
InternalDocument doc = new InternalDocument();
var docElements = CreateElements(ref stack);
docElements.Reverse(); // reverse due to stack
doc.NestedObjects = new InternalObject[docElements.Count];
int pos = 0;
for (int i = 0; i < docElements.Count; i++) {
doc.NestedObjects[i] = docElements[i].SetStartRelativeToParent(pos);
pos += doc.NestedObjects[i].Length;
}
doc.Length = pos;
return doc;
}
#region Heuristic implementation - Inserting place holders into object stream
// Tags used to guide the element creation
static readonly InternalTag StartTagPlaceholder = new InternalTag { OpeningBracket = "<", ClosingBracket = ">" };
static readonly InternalTag EndTagPlaceholder = new InternalTag { OpeningBracket = "</", ClosingBracket = ">" };
class OpenTagStack
{
readonly OpenTagStack prev;
public readonly string Name;
public readonly int IndentationLevel;
readonly int hashCode;
public OpenTagStack()
{
}
private OpenTagStack(OpenTagStack prev, string name, int indentationLevel)
{
this.prev = prev;
this.Name = name;
this.IndentationLevel = indentationLevel;
unchecked {
this.hashCode = prev.hashCode * 27 + (name.GetHashCode() ^ indentationLevel);
}
}
public bool IsEmpty {
get { return prev == null; }
}
public OpenTagStack Push(string name, int indentationLevel)
{
return new OpenTagStack(this, name, indentationLevel);
}
public OpenTagStack Pop()
{
return prev;
}
public override int GetHashCode()
{
return hashCode;
}
public override bool Equals(object obj)
{
OpenTagStack o = obj as OpenTagStack;
if (o != null && hashCode == o.hashCode && IndentationLevel == o.IndentationLevel && Name == o.Name) {
if (prev == o.prev)
return true;
if (prev == null || o.prev == null)
return false;
return prev.Equals(o.prev);
}
return false;
}
}
struct Configuration
{
public readonly OpenTagStack OpenTags;
public readonly ImmutableStack<InternalObject> Document;
public readonly uint Cost;
public Configuration(OpenTagStack openTags, ImmutableStack<InternalObject> document, uint cost)
{
this.OpenTags = openTags;
this.Document = document;
this.Cost = cost;
}
}
struct ConfigurationList
{
internal Configuration[] configurations;
internal int count;
public static ConfigurationList Create()
{
return new ConfigurationList {
configurations = new Configuration[MaxConfigurationCount],
count = 0
};
}
public void Clear()
{
this.count = 0;
}
public void Add(OpenTagStack openTags, ImmutableStack<InternalObject> document, uint cost)
{
Add(new Configuration(openTags, document, cost));
}
public void Add(Configuration configuration)
{
for (int i = 0; i < count; i++) {
if (configuration.OpenTags.Equals(configurations[i].OpenTags)) {
// We found an existing configuration with the same state.
// Either replace it, or drop this configurations --
// we don't want to add multiple configurations with the same state.
if (configuration.Cost < configurations[i].Cost)
configurations[i] = configuration;
return;
}
}
if (count < configurations.Length) {
configurations[count++] = configuration;
} else {
int index = 0;
uint maxCost = configurations[0].Cost;
for (int i = 1; i < configurations.Length; i++) {
if (configurations[i].Cost < maxCost) {
maxCost = configurations[i].Cost;
index = i;
}
}
configurations[index] = configuration;
}
}
}
const uint InfiniteCost = uint.MaxValue;
const uint MissingEndTagCost = 10;
const uint IgnoreEndTagCost = 10;
const uint MismatchedNameCost = 6;
int GetIndentationBefore(int position)
{
int indentation = 0;
while (--position >= 0) {
char c = textSource.GetCharAt(position);
switch (c) {
case ' ':
indentation++;
break;
case '\t':
indentation += 4;
break;
case '\n':
return indentation;
default:
return -1;
}
}
return indentation;
}
ImmutableStack<InternalObject> InsertPlaceholderTags(List<InternalObject> objects, CancellationToken cancellationToken)
{
// Calculate indentation levels in front of the tags:
int[] indentationBeforeTags = new int[objects.Count];
int pos = 0;
for (int i = 0; i < objects.Count; i++) {
if (objects[i] is InternalTag)
indentationBeforeTags[i] = GetIndentationBefore(pos);
pos += objects[i].Length;
}
// Create initial configuration:
ConfigurationList listA = ConfigurationList.Create();
ConfigurationList listB = ConfigurationList.Create();
listA.Add(new Configuration(new OpenTagStack(), ImmutableStack<InternalObject>.Empty, 0));
for (int i = 0; i < indentationBeforeTags.Length; i++) {
cancellationToken.ThrowIfCancellationRequested();
ProcessObject(objects[i], indentationBeforeTags[i], listA, ref listB);
Swap(ref listA, ref listB);
}
Configuration cheapestConfiguration = new Configuration(null, null, InfiniteCost);
for (int i = 0; i < listA.count; i++) {
Configuration c = listA.configurations[i];
if (c.Cost < cheapestConfiguration.Cost) {
while (!c.OpenTags.IsEmpty) {
c = new Configuration(c.OpenTags.Pop(), c.Document.Push(EndTagPlaceholder), c.Cost + MissingEndTagCost);
}
if (c.Cost < cheapestConfiguration.Cost)
cheapestConfiguration = c;
}
}
Log.WriteLine("Best configuration has cost {0}", cheapestConfiguration.Cost);
return cheapestConfiguration.Document;
}
static void Swap(ref ConfigurationList a, ref ConfigurationList b)
{
ConfigurationList tmp = a;
a = b;
b = tmp;
}
void ProcessObject(InternalObject obj, int indentationLevel, ConfigurationList oldConfigurations, ref ConfigurationList newConfigurations)
{
newConfigurations.Clear();
InternalTag tag = obj as InternalTag;
for (int i = 0; i < oldConfigurations.count; i++) {
Configuration c = oldConfigurations.configurations[i];
if (c.Cost == InfiniteCost)
continue;
if (tag != null && tag.IsStartTag) {
// Push start tag
newConfigurations.Add(
c.OpenTags.Push(tag.Name, indentationLevel),
c.Document.Push(obj),
c.Cost
);
} else if (tag != null && tag.IsEndTag) {
// We can ignore this end tag
newConfigurations.Add(
c.OpenTags,
c.Document.Push(StartTagPlaceholder).Push(obj),
c.Cost + IgnoreEndTagCost
);
// We can match this end tag with one of the currently open tags
var openTags = c.OpenTags;
var documentWithInsertedEndTags = c.Document;
uint newCost = c.Cost;
while (!openTags.IsEmpty) {
uint matchCost = 0;
if (openTags.IndentationLevel >= 0 && indentationLevel >= 0)
matchCost += (uint)Math.Abs(openTags.IndentationLevel - indentationLevel);
if (openTags.Name != tag.Name)
matchCost += MismatchedNameCost;
newConfigurations.Add(
openTags.Pop(),
documentWithInsertedEndTags.Push(obj),
newCost + matchCost
);
newCost += MissingEndTagCost;
openTags = openTags.Pop();
documentWithInsertedEndTags = documentWithInsertedEndTags.Push(EndTagPlaceholder);
}
} else {
newConfigurations.Add(
c.OpenTags,
c.Document.Push(obj),
c.Cost
);
}
}
}
#endregion
#region Create Elements from stack with place holders
List<InternalObject> CreateElements(ref ImmutableStack<InternalObject> inputObjects)
{
List<InternalObject> objects = new List<InternalObject>();
while (!inputObjects.IsEmpty) {
var obj = inputObjects.Peek();
var tag = obj as InternalTag;
if (tag != null && tag.IsStartTag)
break;
inputObjects = inputObjects.Pop();
if (tag != null && tag.IsEndTag) {
if (inputObjects.Peek() == StartTagPlaceholder) {
objects.Add(tag.AddSyntaxError("Matching opening tag was not found"));
inputObjects = inputObjects.Pop();
} else {
var childElements = CreateElements(ref inputObjects);
var startTag = (InternalTag)inputObjects.Peek();
inputObjects = inputObjects.Pop();
childElements.Add(startTag);
childElements.Reverse();
if (tag != EndTagPlaceholder) {
// add end tag
if (startTag.Name != tag.Name) {
childElements.Add(tag.AddSyntaxError("Expected '</" + startTag.Name + ">'. End tag must have same name as start tag."));
} else {
childElements.Add(tag);
}
}
InternalElement e = new InternalElement(startTag);
e.HasEndTag = (tag != EndTagPlaceholder);
e.NestedObjects = new InternalObject[childElements.Count];
int pos = 0;
for (int i = 0; i < childElements.Count; i++) {
e.NestedObjects[i] = childElements[i].SetStartRelativeToParent(pos);
pos += e.NestedObjects[i].Length;
}
e.Length = pos;
if (tag == EndTagPlaceholder) {
e.SyntaxErrors = new [] { new InternalSyntaxError(pos, pos, "Missing '</" + startTag.Name + ">'") };
}
objects.Add(e);
}
} else {
objects.Add(obj);
}
}
return objects;
}
#endregion
}
}

834
ICSharpCode.Decompiler/Xml/TagReader.cs

@ -1,834 +0,0 @@ @@ -1,834 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
namespace ICSharpCode.Decompiler.Xml
{
class TagReader : TokenReader
{
readonly AXmlParser tagSoupParser;
readonly Stack<string> elementNameStack;
public TagReader(AXmlParser tagSoupParser, ITextSource input, bool collapseProperlyNestedElements) : base(input)
{
this.tagSoupParser = tagSoupParser;
if (collapseProperlyNestedElements)
elementNameStack = new Stack<string>();
}
public List<InternalObject> ReadAllObjects(CancellationToken cancellationToken)
{
while (HasMoreData()) {
cancellationToken.ThrowIfCancellationRequested();
ReadObject();
}
return objects;
}
public List<InternalObject> ReadAllObjectsIncremental(InternalObject[] oldObjects, List<UnchangedSegment> reuseMap, CancellationToken cancellationToken)
{
ObjectIterator oldObjectIterator = new ObjectIterator(oldObjects);
int reuseMapIndex = 0;
while (reuseMapIndex < reuseMap.Count) {
var reuseEntry = reuseMap[reuseMapIndex];
while (this.CurrentLocation < reuseEntry.NewOffset) {
cancellationToken.ThrowIfCancellationRequested();
ReadObject();
}
if (this.CurrentLocation >= reuseEntry.NewOffset + reuseEntry.Length) {
reuseMapIndex++;
continue;
}
Debug.Assert(reuseEntry.NewOffset <= this.CurrentLocation && this.CurrentLocation < reuseEntry.NewOffset + reuseEntry.Length);
// reuse the nodes within this reuseEntry starting at oldOffset:
int oldOffset = this.CurrentLocation - reuseEntry.NewOffset + reuseEntry.OldOffset;
// seek to oldOffset in the oldObjects array:
oldObjectIterator.SkipTo(oldOffset);
if (oldObjectIterator.CurrentPosition == oldOffset) {
// reuse old objects within this reuse entry:
int reuseEnd = reuseEntry.OldOffset + reuseEntry.Length;
while (oldObjectIterator.CurrentObject != null && oldObjectIterator.CurrentPosition + oldObjectIterator.CurrentObject.LengthTouched < reuseEnd) {
StoreObject(oldObjectIterator.CurrentObject);
Skip(oldObjectIterator.CurrentObject.Length);
oldObjectIterator.MoveNext();
}
reuseMapIndex++; // go to next re-use map
} else {
// We are in a region where old objects are available, but aren't aligned correctly.
// Don't skip this reuse entry, and read a single object so that we can re-align
ReadObject();
}
}
while (HasMoreData()) {
cancellationToken.ThrowIfCancellationRequested();
ReadObject();
}
return objects;
}
void StoreObject(InternalObject obj)
{
objects.Add(obj);
// Now combine properly-nested elements:
if (elementNameStack == null)
return; // parsing tag soup
InternalTag tag = obj as InternalTag;
if (tag == null)
return;
if (tag.IsEmptyTag) {
// the tag is its own element
objects[objects.Count - 1] = new InternalElement(tag) {
Length = tag.Length,
LengthTouched = tag.LengthTouched,
IsPropertyNested = true,
StartRelativeToParent = tag.StartRelativeToParent,
NestedObjects = new [] { tag.SetStartRelativeToParent(0) }
};
} else if (tag.IsStartTag) {
elementNameStack.Push(tag.Name);
} else if (tag.IsEndTag && elementNameStack.Count > 0) {
// Now look for the start element:
int startIndex = objects.Count - 2;
bool ok = false;
string expectedName = elementNameStack.Pop();
if (tag.Name == expectedName) {
while (startIndex > 0) {
var startTag = objects[startIndex] as InternalTag;
if (startTag != null) {
if (startTag.IsStartTag) {
ok = (startTag.Name == expectedName);
break;
} else if (startTag.IsEndTag) {
break;
}
}
startIndex--;
}
}
if (ok) {
// We found a correct nesting, let's create an element:
InternalObject[] nestedObjects = new InternalObject[objects.Count - startIndex];
int oldStartRelativeToParent = objects[startIndex].StartRelativeToParent;
int pos = 0;
int maxLengthTouched = 0;
for (int i = 0; i < nestedObjects.Length; i++) {
nestedObjects[i] = objects[startIndex + i].SetStartRelativeToParent(pos);
maxLengthTouched = Math.Max(maxLengthTouched, pos + nestedObjects[i].LengthTouched);
pos += nestedObjects[i].Length;
}
objects.RemoveRange(startIndex, nestedObjects.Length);
objects.Add(
new InternalElement((InternalTag)nestedObjects[0]) {
HasEndTag = true,
IsPropertyNested = true,
Length = pos,
LengthTouched = maxLengthTouched,
StartRelativeToParent = oldStartRelativeToParent,
NestedObjects = nestedObjects
});
} else {
// Mismatched name - the nesting isn't properly;
// clear the whole stack so that none of the currently open elements are closed as properly-nested.
elementNameStack.Clear();
}
}
}
/// <summary>
/// Reads one or more objects.
/// </summary>
void ReadObject()
{
if (TryPeek('<')) {
ReadTag();
} else {
ReadText(TextType.CharacterData);
}
}
#region BeginInternalObject / EndInternalObject
List<InternalObject> objects = new List<InternalObject>();
int internalObjectStartPosition;
int CurrentRelativeLocation {
get { return CurrentLocation - internalObjectStartPosition; }
}
struct InternalObjectFrame
{
public readonly InternalObject InternalObject;
public readonly int ParentStartPosition;
public InternalObjectFrame(InternalObject internalObject, int parentStartPosition)
{
this.InternalObject = internalObject;
this.ParentStartPosition = parentStartPosition;
}
}
InternalObjectFrame BeginInternalObject(InternalObject internalObject)
{
return BeginInternalObject(internalObject, this.CurrentLocation);
}
InternalObjectFrame BeginInternalObject(InternalObject internalObject, int beginLocation)
{
internalObject.StartRelativeToParent = beginLocation - internalObjectStartPosition;
var frame = new InternalObjectFrame(internalObject, internalObjectStartPosition);
internalObjectStartPosition = CurrentLocation;
return frame;
}
void EndInternalObject(InternalObjectFrame frame, bool storeNewObject = true)
{
frame.InternalObject.Length = this.CurrentLocation - internalObjectStartPosition;
frame.InternalObject.LengthTouched = this.MaxTouchedLocation - internalObjectStartPosition;
frame.InternalObject.SyntaxErrors = GetSyntaxErrors();
if (storeNewObject)
StoreObject(frame.InternalObject);
internalObjectStartPosition = frame.ParentStartPosition;
}
#endregion
#region Read Tag
/// <summary>
/// Context: "&lt;"
/// </summary>
void ReadTag()
{
AssertHasMoreData();
int tagStart = this.CurrentLocation;
InternalTag tag = new InternalTag();
var frame = BeginInternalObject(tag);
// Read the opening bracket
// It identifies the type of tag and parsing behavior for the rest of it
tag.OpeningBracket = ReadOpeningBracket();
if (tag.IsUnknownBang && !TryPeekWhiteSpace())
OnSyntaxError(tagStart, this.CurrentLocation, "Unknown tag");
if (tag.IsStartOrEmptyTag || tag.IsEndTag || tag.IsProcessingInstruction) {
// Read the name
TryMoveToNonWhiteSpace();
tag.RelativeNameStart = this.CurrentRelativeLocation;
string name;
if (TryReadName(out name)) {
if (!IsValidName(name)) {
OnSyntaxError(this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
}
} else {
OnSyntaxError("Element name expected");
}
tag.Name = name;
} else {
tag.Name = string.Empty;
}
bool isXmlDeclr = tag.Name == "xml" && tag.IsProcessingInstruction;
int oldObjectCount = objects.Count;
if (tag.IsStartOrEmptyTag || tag.IsEndTag || isXmlDeclr) {
// Read attributes for the tag
while (HasMoreData()) {
// Chech for all forbiden 'name' characters first - see ReadName
TryMoveToNonWhiteSpace();
if (TryPeek('<')) break;
string endBr;
int endBrStart = this.CurrentLocation; // Just peek
if (TryReadClosingBracket(out endBr)) { // End tag
GoBack(endBrStart);
break;
}
// We have "=\'\"" or name - read attribute
int attrStartOffset = this.CurrentLocation;
ReadAttribute();
if (tag.IsEndTag)
OnSyntaxError(attrStartOffset, this.CurrentLocation, "Attribute not allowed in end tag.");
}
} else if (tag.IsDocumentType) {
ReadContentOfDTD();
} else {
int start = this.CurrentLocation;
if (tag.IsComment) {
ReadText(TextType.Comment);
} else if (tag.IsCData) {
ReadText(TextType.CData);
} else if (tag.IsProcessingInstruction) {
ReadText(TextType.ProcessingInstruction);
} else if (tag.IsUnknownBang) {
ReadText(TextType.UnknownBang);
} else {
throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
}
// Backtrack at complete start
if (IsEndOfFile() || (tag.IsUnknownBang && TryPeek('<'))) {
GoBack(start);
objects.RemoveRange(oldObjectCount, objects.Count - oldObjectCount);
}
}
// Read closing bracket
string bracket;
TryReadClosingBracket(out bracket);
tag.ClosingBracket = bracket;
// Error check
int brStart = this.CurrentLocation - (tag.ClosingBracket ?? string.Empty).Length;
int brEnd = this.CurrentLocation;
if (tag.Name == null) {
// One error was reported already
} else if (tag.IsStartOrEmptyTag) {
if (tag.ClosingBracket != ">" && tag.ClosingBracket != "/>") OnSyntaxError(brStart, brEnd, "'>' or '/>' expected");
} else if (tag.IsEndTag) {
if (tag.ClosingBracket != ">") OnSyntaxError(brStart, brEnd, "'>' expected");
} else if (tag.IsComment) {
if (tag.ClosingBracket != "-->") OnSyntaxError(brStart, brEnd, "'-->' expected");
} else if (tag.IsCData) {
if (tag.ClosingBracket != "]]>") OnSyntaxError(brStart, brEnd, "']]>' expected");
} else if (tag.IsProcessingInstruction) {
if (tag.ClosingBracket != "?>") OnSyntaxError(brStart, brEnd, "'?>' expected");
} else if (tag.IsUnknownBang) {
if (tag.ClosingBracket != ">") OnSyntaxError(brStart, brEnd, "'>' expected");
} else if (tag.IsDocumentType) {
if (tag.ClosingBracket != ">") OnSyntaxError(brStart, brEnd, "'>' expected");
} else {
throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
}
// Attribute name may not apper multiple times
if (objects.Count > oldObjectCount) {
// Move nested objects into tag.NestedObjects:
tag.NestedObjects = new InternalObject[objects.Count - oldObjectCount];
objects.CopyTo(oldObjectCount, tag.NestedObjects, 0, tag.NestedObjects.Length);
objects.RemoveRange(oldObjectCount, objects.Count - oldObjectCount);
// Look for duplicate attributes:
HashSet<string> attributeNames = new HashSet<string>();
foreach (var obj in tag.NestedObjects) {
InternalAttribute attr = obj as InternalAttribute;
if (attr != null && !attributeNames.Add(attr.Name)) {
int attrStart = tagStart + attr.StartRelativeToParent;
OnSyntaxError(attrStart, attrStart + attr.Name.Length, "Attribute with name '{0}' already exists", attr.Name);
}
}
}
EndInternalObject(frame);
}
#endregion
#region Read DTD
void ReadContentOfDTD()
{
int start = this.CurrentLocation;
while (HasMoreData()) {
TryMoveToNonWhiteSpace(); // Skip whitespace
if (TryRead('\'')) TryMoveTo('\''); // Skip single quoted string TODO: Bug
if (TryRead('\"')) TryMoveTo('\"'); // Skip single quoted string
if (TryRead('[')) { // Start of nested infoset
// Reading infoset
while (HasMoreData()) {
TryMoveToAnyOf('<', ']');
if (TryPeek('<')) {
if (start != this.CurrentLocation) { // Two following tags
MakeText(start, this.CurrentLocation);
}
ReadTag();
start = this.CurrentLocation;
}
if (TryPeek(']')) break;
}
}
TryRead(']'); // End of nested infoset
if (TryPeek('>')) break; // Proper closing
if (TryPeek('<')) break; // Malformed XML
TryMoveNext(); // Skip anything else
}
if (start != this.CurrentLocation) {
MakeText(start, this.CurrentLocation);
}
}
void MakeText(int start, int end)
{
Log.DebugAssert(end > start, "Empty text");
Log.DebugAssert(end == this.CurrentLocation, "end == current location");
InternalText text = new InternalText();
var frame = BeginInternalObject(text, start);
text.Type = TextType.Other;
text.Value = GetText(start, end);
EndInternalObject(frame);
}
#endregion
#region Read Brackets
/// <summary>
/// Reads any of the know opening brackets. (only full bracket)
/// Context: "&lt;"
/// </summary>
string ReadOpeningBracket()
{
// We are using a lot of string literals so that the memory instances are shared
//int start = this.CurrentLocation;
if (TryRead('<')) {
if (TryRead('/')) {
return "</";
} else if (TryRead('?')) {
return "<?";
} else if (TryRead('!')) {
if (TryRead("--")) {
return "<!--";
} else if (TryRead("[CDATA[")) {
return "<![CDATA[";
} else {
foreach (string dtdName in AXmlTag.DtdNames) {
// the dtdName includes "<!"
if (TryRead(dtdName.Remove(0, 2))) return dtdName;
}
return "<!";
}
} else {
return "<";
}
} else {
throw new InternalException("'<' expected");
}
}
/// <summary>
/// Reads any of the know closing brackets. (only full bracket)
/// Context: any
/// </summary>
bool TryReadClosingBracket(out string bracket)
{
// We are using a lot of string literals so that the memory instances are shared
if (TryRead('>')) {
bracket = ">";
} else if (TryRead("/>")) {
bracket = "/>";
} else if (TryRead("?>")) {
bracket = "?>";
} else if (TryRead("-->")) {
bracket = "-->";
} else if (TryRead("]]>")) {
bracket = "]]>";
} else {
bracket = string.Empty;
return false;
}
return true;
}
#endregion
#region Attributes
/// <summary>
/// Context: name or "=\'\""
/// </summary>
void ReadAttribute()
{
AssertHasMoreData();
InternalAttribute attr = new InternalAttribute();
var frame = BeginInternalObject(attr);
// Read name
string name;
if (TryReadName(out name)) {
if (!IsValidName(name)) {
OnSyntaxError(this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
}
} else {
OnSyntaxError("Attribute name expected");
}
attr.Name = name;
// Read equals sign and surrounding whitespace
int checkpoint = this.CurrentLocation;
TryMoveToNonWhiteSpace();
if (TryRead('=')) {
int chk2 = this.CurrentLocation;
TryMoveToNonWhiteSpace();
if (!TryPeek('"') && !TryPeek('\'')) {
// Do not read whitespace if quote does not follow
GoBack(chk2);
}
attr.EqualsSignLength = this.CurrentLocation - checkpoint;
} else {
GoBack(checkpoint);
OnSyntaxError("'=' expected");
attr.EqualsSignLength = 0;
}
// Read attribute value
int start = this.CurrentLocation;
char quoteChar = TryPeek('"') ? '"' : '\'';
bool startsWithQuote;
if (TryRead(quoteChar)) {
startsWithQuote = true;
int valueStart = this.CurrentLocation;
TryMoveToAnyOf(quoteChar, '<');
if (TryRead(quoteChar)) {
if (!TryPeekAnyOf(' ', '\t', '\n', '\r', '/', '>', '?')) {
if (TryPeekPrevious('=', 2) || (TryPeekPrevious('=', 3) && TryPeekPrevious(' ', 2))) {
// This actually most likely means that we are in the next attribute value
GoBack(valueStart);
ReadAttributeValue(quoteChar);
if (TryRead(quoteChar)) {
OnSyntaxError("White space or end of tag expected");
} else {
OnSyntaxError("Quote {0} expected (or add whitespace after the following one)", quoteChar);
}
} else {
OnSyntaxError("White space or end of tag expected");
}
}
} else {
// '<' or end of file
GoBack(valueStart);
ReadAttributeValue(quoteChar);
OnSyntaxError("Quote {0} expected", quoteChar);
}
} else {
startsWithQuote = false;
int valueStart = this.CurrentLocation;
ReadAttributeValue(null);
TryRead('\"');
TryRead('\'');
if (valueStart == this.CurrentLocation) {
OnSyntaxError("Attribute value expected");
} else {
OnSyntaxError(valueStart, this.CurrentLocation, "Attribute value must be quoted");
}
}
string val = GetText(start, this.CurrentLocation);
val = Unquote(val);
attr.Value = Dereference(val, startsWithQuote ? start + 1 : start);
EndInternalObject(frame);
}
/// <summary>
/// Read everything up to quote (excluding), opening/closing tag or attribute signature
/// </summary>
void ReadAttributeValue(char? quote)
{
while (HasMoreData()) {
// What is next?
int start = this.CurrentLocation;
TryMoveToNonWhiteSpace(); // Read white space (if any)
if (quote.HasValue) {
if (TryPeek(quote.Value)) return;
} else {
if (TryPeek('"') || TryPeek('\'')) return;
}
// Opening/closing tag
string endBr;
if (TryPeek('<') || TryReadClosingBracket(out endBr)) {
GoBack(start);
return;
}
// Try reading attribute signature
if (TryReadName()) {
int nameEnd = this.CurrentLocation;
if (TryMoveToNonWhiteSpace() && TryRead("=") &&
TryMoveToNonWhiteSpace() && TryPeekAnyOf('"', '\''))
{
// Start of attribute. Great
GoBack(start);
return; // Done
} else {
// Just some gargabe - make it part of the value
GoBack(nameEnd);
continue; // Read more
}
}
TryMoveNext(); // Accept everyting else
}
}
/// <summary> Remove quoting from the given string </summary>
static string Unquote(string quoted)
{
if (string.IsNullOrEmpty(quoted)) return string.Empty;
char first = quoted[0];
if (quoted.Length == 1) return (first == '"' || first == '\'') ? string.Empty : quoted;
char last = quoted[quoted.Length - 1];
if (first == '"' || first == '\'') {
if (first == last) {
// Remove both quotes
return quoted.Substring(1, quoted.Length - 2);
} else {
// Remove first quote
return quoted.Remove(0, 1);
}
} else {
if (last == '"' || last == '\'') {
// Remove last quote
return quoted.Substring(0, quoted.Length - 1);
} else {
// Keep whole string
return quoted;
}
}
}
#endregion
#region Text
/// <summary>
/// Reads text.
/// </summary>
void ReadText(TextType type)
{
var text = new InternalText();
var frame = BeginInternalObject(text);
text.Type = type;
int start = this.CurrentLocation;
int fragmentEnd = inputLength;
// Whitespace would be skipped anyway by any operation
TryMoveToNonWhiteSpace(fragmentEnd);
int wsEnd = this.CurrentLocation;
// Try move to the terminator given by the context
if (type == TextType.WhiteSpace) {
TryMoveToNonWhiteSpace(fragmentEnd);
} else if (type == TextType.CharacterData) {
while(true) {
if (!TryMoveToAnyOf(new char[] {'<', ']'}, fragmentEnd)) break; // End of fragment
if (TryPeek('<')) break;
if (TryPeek(']')) {
if (TryPeek("]]>")) {
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 3, "']]>' is not allowed in text");
}
TryMoveNext();
continue;
}
throw new InternalException("Infinite loop");
}
} else if (type == TextType.Comment) {
// Do not report too many errors
bool errorReported = false;
while(true) {
if (!TryMoveTo('-', fragmentEnd)) break; // End of fragment
if (TryPeek("-->")) break;
if (TryPeek("--") && !errorReported) {
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 2, "'--' is not allowed in comment");
errorReported = true;
}
TryMoveNext();
}
} else if (type == TextType.CData) {
while(true) {
// We can not use use TryMoveTo("]]>", fragmentEnd) because it may incorectly accept "]" at the end of fragment
if (!TryMoveTo(']', fragmentEnd)) break; // End of fragment
if (TryPeek("]]>")) break;
TryMoveNext();
}
} else if (type == TextType.ProcessingInstruction) {
while(true) {
if (!TryMoveTo('?', fragmentEnd)) break; // End of fragment
if (TryPeek("?>")) break;
TryMoveNext();
}
} else if (type == TextType.UnknownBang) {
TryMoveToAnyOf(new char[] {'<', '>'}, fragmentEnd);
} else {
throw new InternalException("Unknown type " + type);
}
text.ContainsOnlyWhitespace = (wsEnd == this.CurrentLocation);
string escapedValue = GetText(start, this.CurrentLocation);
if (type == TextType.CharacterData) {
text.Value = Dereference(escapedValue, start);
} else {
text.Value = escapedValue;
}
text.Value = GetCachedString(text.Value);
EndInternalObject(frame, storeNewObject: this.CurrentLocation > start);
}
#endregion
#region Dereference
const int maxEntityLength = 16; // The longest built-in one is 10 ("&#1114111;")
string Dereference(string text, int textLocation)
{
StringBuilder sb = null; // The dereferenced text so far (all up to 'curr')
int curr = 0;
while(true) {
// Reached end of input
if (curr == text.Length) {
if (sb != null) {
return sb.ToString();
} else {
return text;
}
}
// Try to find reference
int start = text.IndexOf('&', curr);
// No more references found
if (start == -1) {
if (sb != null) {
sb.Append(text, curr, text.Length - curr); // Add rest
return sb.ToString();
} else {
return text;
}
}
// Append text before the enitiy reference
if (sb == null) sb = new StringBuilder(text.Length);
sb.Append(text, curr, start - curr);
curr = start;
// Process the entity
int errorLoc = textLocation + sb.Length;
// Find entity name
int end = text.IndexOfAny(new char[] {'&', ';'}, start + 1, Math.Min(maxEntityLength, text.Length - (start + 1)));
if (end == -1 || text[end] == '&') {
// Not found
OnSyntaxError(errorLoc, errorLoc + 1, "Entity reference must be terminated with ';'");
// Keep '&'
sb.Append('&');
curr++;
continue; // Restart and next character location
}
string name = text.Substring(start + 1, end - (start + 1));
// Resolve the name
string replacement;
if (name.Length == 0) {
replacement = null;
OnSyntaxError(errorLoc + 1, errorLoc + 1, "Entity name expected");
} else if (name == "amp") {
replacement = "&";
} else if (name == "lt") {
replacement = "<";
} else if (name == "gt") {
replacement = ">";
} else if (name == "apos") {
replacement = "'";
} else if (name == "quot") {
replacement = "\"";
} else if (name.Length > 0 && name[0] == '#') {
int num;
if (name.Length > 1 && name[1] == 'x') {
if (!int.TryParse(name.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out num)) {
num = -1;
OnSyntaxError(errorLoc + 3, errorLoc + 1 + name.Length, "Hexadecimal code of unicode character expected");
}
} else {
if (!int.TryParse(name.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out num)) {
num = -1;
OnSyntaxError(errorLoc + 2, errorLoc + 1 + name.Length, "Numeric code of unicode character expected");
}
}
if (num != -1) {
try {
replacement = char.ConvertFromUtf32(num);
} catch (ArgumentOutOfRangeException) {
replacement = null;
OnSyntaxError(errorLoc + 2, errorLoc + 1 + name.Length, "Invalid unicode character U+{0:X} ({0})", num);
}
} else {
replacement = null;
}
} else if (!IsValidName(name)) {
replacement = null;
OnSyntaxError(errorLoc + 1, errorLoc + 1, "Invalid entity name");
} else {
replacement = null;
if (tagSoupParser.UnknownEntityReferenceIsError) {
OnSyntaxError(errorLoc, errorLoc + 1 + name.Length + 1, "Unknown entity reference '{0}'", name);
}
}
// Append the replacement to output
if (replacement != null) {
sb.Append(replacement);
} else {
sb.Append('&');
sb.Append(name);
sb.Append(';');
}
curr = end + 1;
continue;
}
}
#endregion
#region Syntax Errors
List<InternalSyntaxError> syntaxErrors = new List<InternalSyntaxError>();
InternalSyntaxError[] GetSyntaxErrors()
{
if (syntaxErrors.Count > 0) {
var arr = syntaxErrors.ToArray();
syntaxErrors.Clear();
return arr;
} else {
return null;
}
}
void OnSyntaxError(string message, params object[] args)
{
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 1, message, args);
}
void OnSyntaxError(int start, int end, string message, params object[] args)
{
if (end <= start) end = start + 1;
string formattedMessage = string.Format(CultureInfo.InvariantCulture, message, args);
Log.WriteLine("Syntax error ({0}-{1}): {2}", start, end, formattedMessage);
syntaxErrors.Add(new InternalSyntaxError(start - internalObjectStartPosition, end - internalObjectStartPosition, formattedMessage));
}
#endregion
#region Helper functions
internal static bool IsValidName(string name)
{
try {
System.Xml.XmlConvert.VerifyName(name);
return true;
} catch (System.Xml.XmlException) {
return false;
}
}
#endregion
}
}

100
ICSharpCode.Decompiler/Xml/TextChangeEventArgs.cs

@ -1,100 +0,0 @@ @@ -1,100 +0,0 @@
using System;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// Describes a change of the document text.
/// This class is thread-safe.
/// </summary>
[Serializable]
public class TextChangeEventArgs : EventArgs
{
readonly int offset;
readonly ITextSource removedText;
readonly ITextSource insertedText;
/// <summary>
/// The offset at which the change occurs.
/// </summary>
public int Offset {
get { return offset; }
}
/// <summary>
/// The text that was removed.
/// </summary>
public ITextSource RemovedText {
get { return removedText; }
}
/// <summary>
/// The number of characters removed.
/// </summary>
public int RemovalLength {
get { return removedText.TextLength; }
}
/// <summary>
/// The text that was inserted.
/// </summary>
public ITextSource InsertedText {
get { return insertedText; }
}
/// <summary>
/// The number of characters inserted.
/// </summary>
public int InsertionLength {
get { return insertedText.TextLength; }
}
/// <summary>
/// Creates a new TextChangeEventArgs object.
/// </summary>
public TextChangeEventArgs(int offset, string removedText, string insertedText)
{
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative");
this.offset = offset;
this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty;
this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty;
}
/// <summary>
/// Creates a new TextChangeEventArgs object.
/// </summary>
public TextChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText)
{
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative");
this.offset = offset;
this.removedText = removedText ?? StringTextSource.Empty;
this.insertedText = insertedText ?? StringTextSource.Empty;
}
/// <summary>
/// Gets the new offset where the specified offset moves after this document change.
/// </summary>
public virtual int GetNewOffset(int offset, AnchorMovementType movementType = AnchorMovementType.Default)
{
if (offset >= this.Offset && offset <= this.Offset + this.RemovalLength) {
if (movementType == AnchorMovementType.BeforeInsertion)
return this.Offset;
else
return this.Offset + this.InsertionLength;
} else if (offset > this.Offset) {
return offset + this.InsertionLength - this.RemovalLength;
} else {
return offset;
}
}
/// <summary>
/// Creates TextChangeEventArgs for the reverse change.
/// </summary>
public virtual TextChangeEventArgs Invert()
{
return new TextChangeEventArgs(offset, insertedText, removedText);
}
}
}

219
ICSharpCode.Decompiler/Xml/TextLocation.cs

@ -1,219 +0,0 @@ @@ -1,219 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Globalization;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary>
/// A line/column position.
/// Text editor lines/columns are counted started from one.
/// </summary>
[Serializable]
[TypeConverter(typeof(TextLocationConverter))]
public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation>
{
/// <summary>
/// Represents no text location (0, 0).
/// </summary>
public static readonly TextLocation Empty = new TextLocation(0, 0);
/// <summary>
/// Constant of the minimum line.
/// </summary>
public const int MinLine = 1;
/// <summary>
/// Constant of the minimum column.
/// </summary>
public const int MinColumn = 1;
/// <summary>
/// Creates a TextLocation instance.
/// </summary>
public TextLocation(int line, int column)
{
this.line = line;
this.column = column;
}
int column, line;
/// <summary>
/// Gets the line number.
/// </summary>
public int Line {
get { return line; }
}
/// <summary>
/// Gets the column number.
/// </summary>
public int Column {
get { return column; }
}
/// <summary>
/// Gets whether the TextLocation instance is empty.
/// </summary>
public bool IsEmpty {
get {
return column < MinLine && line < MinColumn;
}
}
/// <summary>
/// Gets a string representation for debugging purposes.
/// </summary>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.column, this.line);
}
/// <summary>
/// Gets a hash code.
/// </summary>
public override int GetHashCode()
{
return unchecked(191 * column.GetHashCode() ^ line.GetHashCode());
}
/// <summary>
/// Equality test.
/// </summary>
public override bool Equals(object obj)
{
if (!(obj is TextLocation)) return false;
return (TextLocation)obj == this;
}
/// <summary>
/// Equality test.
/// </summary>
public bool Equals(TextLocation other)
{
return this == other;
}
/// <summary>
/// Equality test.
/// </summary>
public static bool operator ==(TextLocation left, TextLocation right)
{
return left.column == right.column && left.line == right.line;
}
/// <summary>
/// Inequality test.
/// </summary>
public static bool operator !=(TextLocation left, TextLocation right)
{
return left.column != right.column || left.line != right.line;
}
/// <summary>
/// Compares two text locations.
/// </summary>
public static bool operator <(TextLocation left, TextLocation right)
{
if (left.line < right.line)
return true;
else if (left.line == right.line)
return left.column < right.column;
else
return false;
}
/// <summary>
/// Compares two text locations.
/// </summary>
public static bool operator >(TextLocation left, TextLocation right)
{
if (left.line > right.line)
return true;
else if (left.line == right.line)
return left.column > right.column;
else
return false;
}
/// <summary>
/// Compares two text locations.
/// </summary>
public static bool operator <=(TextLocation left, TextLocation right)
{
return !(left > right);
}
/// <summary>
/// Compares two text locations.
/// </summary>
public static bool operator >=(TextLocation left, TextLocation right)
{
return !(left < right);
}
/// <summary>
/// Compares two text locations.
/// </summary>
public int CompareTo(TextLocation other)
{
if (this == other)
return 0;
if (this < other)
return -1;
else
return 1;
}
}
public class TextLocationConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(TextLocation) || base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string) {
string[] parts = ((string)value).Split(';', ',');
if (parts.Length == 2) {
return new TextLocation(int.Parse(parts[0]), int.Parse(parts[1]));
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is TextLocation) {
var loc = (TextLocation)value;
return loc.Line + ";" + loc.Column;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}

47
ICSharpCode.Decompiler/Xml/TextType.cs

@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Xml
{
/// <summary> Identifies the context in which the text occured </summary>
enum TextType
{
/// <summary> Ends with non-whitespace </summary>
WhiteSpace,
/// <summary> Ends with "&lt;"; "]]&gt;" is error </summary>
CharacterData,
/// <summary> Ends with "-->"; "--" is error </summary>
Comment,
/// <summary> Ends with "]]&gt;" </summary>
CData,
/// <summary> Ends with "?>" </summary>
ProcessingInstruction,
/// <summary> Ends with "&lt;" or ">" </summary>
UnknownBang,
/// <summary> Unknown </summary>
Other
}
}

349
ICSharpCode.Decompiler/Xml/TokenReader.cs

@ -1,349 +0,0 @@ @@ -1,349 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.Xml
{
class TokenReader
{
protected readonly ITextSource input;
protected readonly int inputLength;
int currentLocation;
// CurrentLocation is assumed to be touched and the fact does not
// have to be recorded in this variable.
// This stores any value bigger than that if applicable.
// Actual value is max(currentLocation, maxTouchedLocation).
int maxTouchedLocation;
public int InputLength {
get { return inputLength; }
}
public int CurrentLocation {
get { return currentLocation; }
}
public int MaxTouchedLocation {
// add 1 to currentLocation because single-char-peek does not increment maxTouchedLocation
get { return Math.Max(currentLocation + 1, maxTouchedLocation); }
}
public TokenReader(ITextSource input)
{
this.input = input;
this.inputLength = input.TextLength;
}
protected bool IsEndOfFile()
{
return currentLocation == inputLength;
}
protected bool HasMoreData()
{
return currentLocation < inputLength;
}
protected void AssertHasMoreData()
{
Log.Assert(HasMoreData(), "Unexpected end of file");
}
protected bool TryMoveNext()
{
if (currentLocation == inputLength) return false;
currentLocation++;
return true;
}
protected void Skip(int count)
{
Log.Assert(currentLocation + count <= inputLength, "Skipping after the end of file");
currentLocation += count;
}
protected void GoBack(int oldLocation)
{
Log.Assert(oldLocation <= currentLocation, "Trying to move forward");
// add 1 because single-char-peek does not increment maxTouchedLocation
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation + 1);
currentLocation = oldLocation;
}
protected bool TryRead(char c)
{
if (currentLocation == inputLength) return false;
if (input.GetCharAt(currentLocation) == c) {
currentLocation++;
return true;
} else {
return false;
}
}
protected bool TryReadAnyOf(params char[] c)
{
if (currentLocation == inputLength) return false;
if (c.Contains(input.GetCharAt(currentLocation))) {
currentLocation++;
return true;
} else {
return false;
}
}
protected bool TryRead(string text)
{
if (TryPeek(text)) {
currentLocation += text.Length;
return true;
} else {
return false;
}
}
protected bool TryPeekPrevious(char c, int back)
{
if (currentLocation - back == inputLength) return false;
if (currentLocation - back < 0 ) return false;
return input.GetCharAt(currentLocation - back) == c;
}
protected bool TryPeek(char c)
{
if (currentLocation == inputLength) return false;
return input.GetCharAt(currentLocation) == c;
}
protected bool TryPeekAnyOf(params char[] chars)
{
if (currentLocation == inputLength) return false;
return chars.Contains(input.GetCharAt(currentLocation));
}
protected bool TryPeek(string text)
{
if (!TryPeek(text[0])) return false; // Early exit
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation + text.Length);
// The following comparison 'touches' the end of file - it does depend on the end being there
if (currentLocation + text.Length > inputLength) return false;
return input.GetText(currentLocation, text.Length) == text;
}
protected bool TryPeekWhiteSpace()
{
if (currentLocation == inputLength) return false;
char c = input.GetCharAt(currentLocation);
return ((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}
// The move functions do not have to move if already at target
// The move functions allow 'overriding' of the document length
protected bool TryMoveTo(char c)
{
return TryMoveTo(c, inputLength);
}
protected bool TryMoveTo(char c, int inputLength)
{
if (currentLocation == inputLength) return false;
int index = input.IndexOf(c, currentLocation, inputLength - currentLocation);
if (index != -1) {
currentLocation = index;
return true;
} else {
currentLocation = inputLength;
return false;
}
}
protected bool TryMoveToAnyOf(params char[] c)
{
return TryMoveToAnyOf(c, inputLength);
}
protected bool TryMoveToAnyOf(char[] c, int inputLength)
{
if (currentLocation == inputLength) return false;
int index = input.IndexOfAny(c, currentLocation, inputLength - currentLocation);
if (index != -1) {
currentLocation = index;
return true;
} else {
currentLocation = inputLength;
return false;
}
}
protected bool TryMoveTo(string text)
{
return TryMoveTo(text, inputLength);
}
protected bool TryMoveTo(string text, int inputLength)
{
if (currentLocation == inputLength) return false;
int index = input.IndexOf(text, currentLocation, inputLength - currentLocation, StringComparison.Ordinal);
if (index != -1) {
maxTouchedLocation = index + text.Length;
currentLocation = index;
return true;
} else {
currentLocation = inputLength;
return false;
}
}
protected bool TryMoveToNonWhiteSpace()
{
return TryMoveToNonWhiteSpace(inputLength);
}
protected bool TryMoveToNonWhiteSpace(int inputLength)
{
while(true) {
if (currentLocation == inputLength) return false; // Reject end of file
char c = input.GetCharAt(currentLocation);
if (((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
currentLocation++; // Accept white-space
continue;
} else {
return true; // Found non-white-space
}
}
}
/// <summary>
/// Read a name token.
/// The following characters are not allowed:
/// "" End of file
/// " \n\r\t" Whitesapce
/// "=\'\"" Attribute value
/// "&lt;>/?" Tags
/// </summary>
/// <returns>Returns the length of the name</returns>
protected bool TryReadName()
{
int start = currentLocation;
// Keep reading up to invalid character
while (HasMoreData()) {
char c = input.GetCharAt(currentLocation);
if (0x41 <= (int)c) { // Accpet from 'A' onwards
currentLocation++;
continue;
}
if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || // Reject whitesapce
c == '=' || c == '\'' || c == '"' || // Reject attributes
c == '<' || c == '>' || c == '/' || c == '?') { // Reject tags
break;
} else {
currentLocation++;
continue; // Accept other character
}
}
return currentLocation > start;
}
protected bool TryReadName(out string name)
{
int start = currentLocation;
if (TryReadName()) {
name = GetCachedString(GetText(start, currentLocation));
return true;
} else {
name = string.Empty;
return false;
}
}
protected string GetText(int start, int end)
{
Log.Assert(end <= currentLocation, "Reading ahead of current location");
return input.GetText(start, end - start);
}
Dictionary<string, string> stringCache = new Dictionary<string, string>();
#if DEBUG
int stringCacheRequestedCount;
int stringCacheRequestedSize;
int stringCacheStoredCount;
int stringCacheStoredSize;
#endif
internal void PrintStringCacheStats()
{
#if DEBUG
Log.WriteLine("String cache: Requested {0} ({1} bytes); Actaully stored {2} ({3} bytes); {4}% stored", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheStoredCount, stringCacheStoredSize, stringCacheRequestedSize == 0 ? 0 : stringCacheStoredSize * 100 / stringCacheRequestedSize);
#endif
}
[Conditional("DEBUG")]
void AddToRequestedSize(string text)
{
#if DEBUG
stringCacheRequestedCount += 1;
stringCacheRequestedSize += 8 + 2 * text.Length;
#endif
}
[Conditional("DEBUG")]
void AddToStoredSize(string text)
{
#if DEBUG
stringCacheStoredCount += 1;
stringCacheStoredSize += 8 + 2 * text.Length;
#endif
}
protected string GetCachedString(string cached)
{
AddToRequestedSize(cached);
// Do not bother with long strings
if (cached.Length > 32) {
AddToStoredSize(cached);
return cached;
}
string result;
if (stringCache.TryGetValue(cached, out result)) {
// Get the instance from the cache instead
return result;
} else {
// Add to cache
AddToStoredSize(cached);
stringCache.Add(cached, cached);
return cached;
}
}
}
}

47
ICSharpCode.Decompiler/Xml/XmlSegment.cs

@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Xml
{
sealed class XmlSegment : ISegment
{
readonly int startOffset, endOffset;
public XmlSegment(int startOffset, int endOffset)
{
if (endOffset < startOffset)
throw new ArgumentOutOfRangeException();
this.startOffset = startOffset;
this.endOffset = endOffset;
}
int ISegment.Offset {
get { return startOffset; }
}
int ISegment.Length {
get { return endOffset - startOffset; }
}
int ISegment.EndOffset {
get { return endOffset; }
}
}
}

8
ILSpy/TextView/XmlDocRenderer.cs

@ -28,12 +28,13 @@ using System.Windows.Controls; @@ -28,12 +28,13 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Xml;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy.TextView
@ -129,9 +130,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -129,9 +130,8 @@ namespace ICSharpCode.ILSpy.TextView
if (xmlDocumentation == null)
return;
Debug.WriteLine(xmlDocumentation);
AXmlParser parser = new AXmlParser();
var doc = parser.Parse(new Decompiler.Xml.StringTextSource(xmlDocumentation));
AddDocumentationElement(new XmlDocumentationElement(doc, declaringEntity, resolver));
var xml = XElement.Parse("<doc>" + xmlDocumentation + "</doc>");
AddDocumentationElement(new XmlDocumentationElement(xml, declaringEntity, resolver));
}

Loading…
Cancel
Save