mirror of https://github.com/icsharpcode/ILSpy.git
29 changed files with 28 additions and 4617 deletions
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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 "<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"); |
||||
} |
||||
} |
||||
} |
@ -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; } |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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 "<" 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 "<" </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 ">" </summary>
|
||||
public string ClosingBracket { |
||||
get { return internalObject.ClosingBracket; } |
||||
} |
||||
|
||||
/// <summary> True if tag starts with "<" </summary>
|
||||
public bool IsStartOrEmptyTag { get { return internalObject.IsStartOrEmptyTag; } } |
||||
/// <summary> True if tag starts with "<" and ends with ">" </summary>
|
||||
public bool IsStartTag { get { return internalObject.IsStartTag; } } |
||||
/// <summary> True if tag starts with "<" and does not end with ">" </summary>
|
||||
public bool IsEmptyTag { get { return internalObject.IsEmptyTag; } } |
||||
/// <summary> True if tag starts with "</" </summary>
|
||||
public bool IsEndTag { get { return internalObject.IsEndTag; } } |
||||
/// <summary> True if tag starts with "<?" </summary>
|
||||
public bool IsProcessingInstruction { get { return internalObject.IsProcessingInstruction; } } |
||||
/// <summary> True if tag starts with "<!--" </summary>
|
||||
public bool IsComment { get { return internalObject.IsComment; } } |
||||
/// <summary> True if tag starts with "<![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 "<!" </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); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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 "<" </summary>
|
||||
public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } } |
||||
/// <summary> True if tag starts with "<" and ends with ">" </summary>
|
||||
public bool IsStartTag { get { return OpeningBracket == "<" && ClosingBracket == ">"; } } |
||||
/// <summary> True if tag starts with "<" and does not end with ">" </summary>
|
||||
public bool IsEmptyTag { get { return OpeningBracket == "<" && ClosingBracket != ">" ; } } |
||||
/// <summary> True if tag starts with "</" </summary>
|
||||
public bool IsEndTag { get { return OpeningBracket == "</"; } } |
||||
/// <summary> True if tag starts with "<?" </summary>
|
||||
public bool IsProcessingInstruction { get { return OpeningBracket == "<?"; } } |
||||
/// <summary> True if tag starts with "<!--" </summary>
|
||||
public bool IsComment { get { return OpeningBracket == "<!--"; } } |
||||
/// <summary> True if tag starts with "<![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 "<!" </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(); |
||||
} |
||||
} |
||||
} |
@ -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) |
||||
{ |
||||
} |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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; } |
||||
} |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
@ -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: "<"
|
||||
/// </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: "<"
|
||||
/// </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 ("")
|
||||
|
||||
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
|
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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 "<"; "]]>" is error </summary>
|
||||
CharacterData, |
||||
|
||||
/// <summary> Ends with "-->"; "--" is error </summary>
|
||||
Comment, |
||||
|
||||
/// <summary> Ends with "]]>" </summary>
|
||||
CData, |
||||
|
||||
/// <summary> Ends with "?>" </summary>
|
||||
ProcessingInstruction, |
||||
|
||||
/// <summary> Ends with "<" or ">" </summary>
|
||||
UnknownBang, |
||||
|
||||
/// <summary> Unknown </summary>
|
||||
Other |
||||
} |
||||
} |
@ -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
|
||||
/// "<>/?" 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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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; } |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue