Browse Source

remove AvalonEdit.Xml

newNRvisualizers
Siegfried Pammer 14 years ago
parent
commit
d9e170468c
  1. 34
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  2. 129
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs
  3. 119
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs
  4. 282
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs
  5. 69
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs
  6. 226
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs
  7. 266
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs
  8. 90
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObjectCollection.cs
  9. 21
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObjectEventArgs.cs
  10. 201
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs
  11. 108
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs
  12. 62
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs
  13. 44
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AbstractAXmlVisitor.cs
  14. 119
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs
  15. 99
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/FilteredCollection.cs
  16. 29
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/IAXmlVisitor.cs
  17. 45
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs
  18. 70
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/MergedCollection.cs
  19. 69
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs
  20. 36
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/SyntaxError.cs
  21. 740
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs
  22. 39
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TextType.cs
  23. 309
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TokenReader.cs
  24. 165
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs

34
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -362,40 +362,6 @@ @@ -362,40 +362,6 @@
<Compile Include="Utils\ThrowUtil.cs" />
<Compile Include="Utils\Win32.cs" />
<CodeAnalysisDictionary Include="Properties\CodeAnalysisDictionary.xml" />
<Compile Include="Xml\AbstractAXmlVisitor.cs" />
<Compile Include="Xml\AXmlAttribute.cs" />
<Compile Include="Xml\AXmlAttributeCollection.cs" />
<Compile Include="Xml\AXmlContainer.cs" />
<Compile Include="Xml\AXmlDocument.cs" />
<Compile Include="Xml\AXmlElement.cs" />
<Compile Include="Xml\AXmlObject.cs" />
<Compile Include="Xml\AXmlObjectCollection.cs" />
<Compile Include="Xml\AXmlObjectEventArgs.cs" />
<Compile Include="Xml\AXmlParser.cs" />
<Compile Include="Xml\AXmlTag.cs" />
<Compile Include="Xml\AXmlText.cs" />
<Compile Include="Xml\CanonicalPrintAXmlVisitor.cs" />
<Compile Include="Xml\InternalException.cs" />
<Compile Include="Xml\TrackedSegmentCollection.cs">
<DependentUpon>AXmlParser.cs</DependentUpon>
</Compile>
<Compile Include="Xml\FilteredCollection.cs" />
<Compile Include="Xml\IAXmlVisitor.cs" />
<Compile Include="Xml\MergedCollection.cs" />
<Compile Include="Xml\PrettyPrintAXmlVisitor.cs" />
<Compile Include="Xml\SyntaxError.cs" />
<Compile Include="Xml\TagMatchingHeuristics.cs">
<DependentUpon>AXmlParser.cs</DependentUpon>
</Compile>
<Compile Include="Xml\TagReader.cs">
<DependentUpon>AXmlParser.cs</DependentUpon>
</Compile>
<Compile Include="Xml\TextType.cs">
<DependentUpon>AXmlText.cs</DependentUpon>
</Compile>
<Compile Include="Xml\TokenReader.cs">
<DependentUpon>AXmlParser.cs</DependentUpon>
</Compile>
<Resource Include="themes\RightArrow.cur" />
<EmbeddedResource Include="Highlighting\Resources\ASPX.xshd" />
<EmbeddedResource Include="Highlighting\Resources\Boo.xshd" />

129
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs

@ -1,129 +0,0 @@ @@ -1,129 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Name-value pair in a tag
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
public class AXmlAttribute: AXmlObject
{
/// <summary> Name with namespace prefix - exactly as in source file </summary>
public string Name { get; internal set; }
/// <summary> Equals sign and surrounding whitespace </summary>
public string EqualsSign { get; internal set; }
/// <summary> The raw value - exactly as in source file (*probably* quoted and escaped) </summary>
public string QuotedValue { get; internal set; }
/// <summary> Unquoted and dereferenced value of the attribute </summary>
public string Value { get; internal set; }
internal override void DebugCheckConsistency(bool checkParentPointers)
{
DebugAssert(Name != null, "Null Name");
DebugAssert(EqualsSign != null, "Null EqualsSign");
DebugAssert(QuotedValue != null, "Null QuotedValue");
DebugAssert(Value != null, "Null Value");
base.DebugCheckConsistency(checkParentPointers);
}
#region Helpper methods
/// <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.ResolvePrefix(this.Prefix);
}
return NoNamespace; // Orphaned attribute
}
}
/// <summary> Attribute is declaring namespace ("xmlns" or "xmlns:*") </summary>
public bool IsNamespaceDeclaration {
get {
return this.Name == "xmlns" || this.Prefix == "xmlns";
}
}
#endregion
/// <inheritdoc/>
public override void AcceptVisitor(IAXmlVisitor visitor)
{
visitor.VisitAttribute(this);
}
/// <inheritdoc/>
internal override bool UpdateDataFrom(AXmlObject source)
{
if (!base.UpdateDataFrom(source)) return false;
AXmlAttribute src = (AXmlAttribute)source;
if (this.Name != src.Name ||
this.EqualsSign != src.EqualsSign ||
this.QuotedValue != src.QuotedValue ||
this.Value != src.Value)
{
OnChanging();
this.Name = src.Name;
this.EqualsSign = src.EqualsSign;
this.QuotedValue = src.QuotedValue;
this.Value = src.Value;
OnChanged();
return true;
} else {
return false;
}
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value);
}
}
}

119
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs

@ -1,119 +0,0 @@ @@ -1,119 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Specailized attribute collection with attribute name caching
/// </summary>
public class AXmlAttributeCollection: FilteredCollection<AXmlAttribute, AXmlObjectCollection<AXmlObject>>
{
/// <summary> Empty unbound collection </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "InsertItem prevents modifying the Empty collection")]
public static readonly AXmlAttributeCollection Empty = new AXmlAttributeCollection();
/// <summary> Create unbound collection </summary>
protected AXmlAttributeCollection() {}
/// <summary> Wrap the given collection. Non-attributes are filtered </summary>
public AXmlAttributeCollection(AXmlObjectCollection<AXmlObject> source): base(source) {}
/// <summary> Wrap the given collection. Non-attributes are filtered. Items not matching the condition are filtered. </summary>
public AXmlAttributeCollection(AXmlObjectCollection<AXmlObject> source, Predicate<object> condition): base(source, condition) {}
Dictionary<string, List<AXmlAttribute>> hashtable = new Dictionary<string, List<AXmlAttribute>>();
void AddToHashtable(AXmlAttribute attr)
{
string localName = attr.LocalName;
if (!hashtable.ContainsKey(localName)) {
hashtable[localName] = new List<AXmlAttribute>(1);
}
hashtable[localName].Add(attr);
}
void RemoveFromHashtable(AXmlAttribute attr)
{
string localName = attr.LocalName;
hashtable[localName].Remove(attr);
}
static List<AXmlAttribute> NoAttributes = new List<AXmlAttribute>();
/// <summary>
/// Get all attributes with given local name.
/// Hash table is used for lookup so this is cheap.
/// </summary>
public IEnumerable<AXmlAttribute> GetByLocalName(string localName)
{
if (hashtable.ContainsKey(localName)) {
return hashtable[localName];
} else {
return NoAttributes;
}
}
/// <inheritdoc/>
protected override void ClearItems()
{
foreach(AXmlAttribute item in this) {
RemoveFromHashtable(item);
item.Changing -= item_Changing;
item.Changed -= item_Changed;
}
base.ClearItems();
}
/// <inheritdoc/>
protected override void InsertItem(int index, AXmlAttribute item)
{
// prevent insertions into the static 'Empty' instance
if (this == Empty)
throw new NotSupportedException("Cannot insert into AXmlAttributeCollection.Empty");
AddToHashtable(item);
item.Changing += item_Changing;
item.Changed += item_Changed;
base.InsertItem(index, item);
}
/// <inheritdoc/>
protected override void RemoveItem(int index)
{
RemoveFromHashtable(this[index]);
this[index].Changing -= item_Changing;
this[index].Changed -= item_Changed;
base.RemoveItem(index);
}
/// <inheritdoc/>
protected override void SetItem(int index, AXmlAttribute item)
{
RemoveFromHashtable(this[index]);
this[index].Changing -= item_Changing;
this[index].Changed -= item_Changed;
AddToHashtable(item);
item.Changing += item_Changing;
item.Changed += item_Changed;
base.SetItem(index, item);
}
// Every item in the collection should be registered to these handlers
// so that we can handle renames
void item_Changing(object sender, AXmlObjectEventArgs e)
{
RemoveFromHashtable((AXmlAttribute)e.Object);
}
void item_Changed(object sender, AXmlObjectEventArgs e)
{
AddToHashtable((AXmlAttribute)e.Object);
}
}
}

282
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs

@ -1,282 +0,0 @@ @@ -1,282 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Abstact base class for all types that can contain child nodes
/// </summary>
public abstract class AXmlContainer: AXmlObject
{
/// <summary>
/// Children of the node. It is read-only.
/// Note that is has CollectionChanged event.
/// </summary>
public AXmlObjectCollection<AXmlObject> Children { get; private set; }
/// <summary> Create new container </summary>
protected AXmlContainer()
{
this.Children = new AXmlObjectCollection<AXmlObject>();
}
#region Helpper methods
ObservableCollection<AXmlElement> elements;
/// <summary> Gets direcly nested elements (non-recursive) </summary>
public ObservableCollection<AXmlElement> Elements {
get {
if (elements == null) {
elements = new FilteredCollection<AXmlElement, AXmlObjectCollection<AXmlObject>>(this.Children);
}
return elements;
}
}
internal AXmlObject FirstChild {
get {
return this.Children[0];
}
}
internal AXmlObject LastChild {
get {
return this.Children[this.Children.Count - 1];
}
}
#endregion
/// <inheritdoc/>
public override IEnumerable<AXmlObject> GetSelfAndAllChildren()
{
return NRefactory.Utils.TreeTraversal.PreOrder(this,
delegate(AXmlObject i) {
AXmlContainer container = i as AXmlContainer;
if (container != null)
return container.Children;
else
return null;
}
);
}
/// <summary>
/// Gets a child fully containg the given offset.
/// Goes recursively down the tree.
/// Specail case if at the end of attribute or text
/// </summary>
public AXmlObject GetChildAtOffset(int offset)
{
foreach(AXmlObject child in this.Children) {
if ((child is AXmlAttribute || child is AXmlText) && offset == child.EndOffset) return child;
if (child.StartOffset < offset && offset < child.EndOffset) {
AXmlContainer container = child as AXmlContainer;
if (container != null) {
return container.GetChildAtOffset(offset);
} else {
return child;
}
}
}
return this; // No childs at offset
}
// Only these four methods should be used to modify the collection
/// <summary> To be used exlucively by the parser </summary>
internal void AddChild(AXmlObject item)
{
// Childs can be only added to newly parsed items
Assert(this.Parent == null, "I have to be new");
Assert(item.IsCached, "Added item must be in cache");
// Do not set parent pointer
this.Children.InsertItemAt(this.Children.Count, item);
}
/// <summary> To be used exlucively by the parser </summary>
internal void AddChildren(IEnumerable<AXmlObject> items)
{
// Childs can be only added to newly parsed items
Assert(this.Parent == null, "I have to be new");
// Do not set parent pointer
this.Children.InsertItemsAt(this.Children.Count, items.ToList());
}
/// <summary>
/// To be used exclusively by the children update algorithm.
/// Insert child and keep links consistent.
/// </summary>
void InsertChild(int index, AXmlObject item)
{
AXmlParser.Log("Inserting {0} at index {1}", item, index);
Assert(this.Document != null, "Can not insert to dangling object");
Assert(item.Parent != this, "Can not own item twice");
SetParentPointersInTree(item);
this.Children.InsertItemAt(index, item);
this.Document.OnObjectInserted(index, item);
}
/// <summary> Recursively fix all parent pointer in a tree </summary>
/// <remarks>
/// Cache constraint:
/// If cached item has parent set, then the whole subtree must be consistent and document set
/// </remarks>
void SetParentPointersInTree(AXmlObject item)
{
// All items come from the parser cache
if (item.Parent == null) {
// Dangling object - either a new parser object or removed tree (still cached)
item.Parent = this;
item.Document = this.Document;
AXmlContainer container = item as AXmlContainer;
if (container != null) {
foreach(AXmlObject child in container.Children) {
container.SetParentPointersInTree(child);
}
}
} else if (item.Parent == this) {
// If node is attached and then deattached, it will have null parent pointer
// but valid subtree - so its children will alredy have correct parent pointer
// like in this case
// item.DebugCheckConsistency(false);
// Rest of the tree is consistent - do not recurse
} else {
// From cache & parent set => consitent subtree
// item.DebugCheckConsistency(false);
// The parent (or any futher parents) can not be part of parsed document
// becuase otherwise this item would be included twice => safe to change parents
// Maintain cache constraint by setting parents to null
foreach(AXmlObject ancest in item.GetAncestors().ToList()) {
ancest.Parent = null;
}
item.Parent = this;
// Rest of the tree is consistent - do not recurse
}
}
/// <summary>
/// To be used exclusively by the children update algorithm.
/// Remove child, set parent to null and notify the document
/// </summary>
void RemoveChild(int index)
{
AXmlObject removed = this.Children[index];
AXmlParser.Log("Removing {0} at index {1}", removed, index);
// Stop tracking if the object can not be used again
if (!removed.IsCached)
this.Document.Parser.TrackedSegments.RemoveParsedObject(removed);
// Null parent pointer
Assert(removed.Parent == this, "Inconsistent child");
removed.Parent = null;
this.Children.RemoveItemAt(index);
this.Document.OnObjectRemoved(index, removed);
}
/// <summary> Verify that the subtree is consistent. Only in debug build. </summary>
/// <remarks> Parent pointers might be null or pointing somewhere else in parse tree </remarks>
internal override void DebugCheckConsistency(bool checkParentPointers)
{
base.DebugCheckConsistency(checkParentPointers);
AXmlObject prevChild = null;
int myStartOffset = this.StartOffset;
int myEndOffset = this.EndOffset;
foreach(AXmlObject child in this.Children) {
Assert(child.Length != 0, "Empty child");
if (checkParentPointers) {
Assert(child.Parent != null, "Null parent reference");
Assert(child.Parent == this, "Inccorect parent reference");
}
if (this.Document != null) {
Assert(child.Document != null, "Child has null document");
Assert(child.Document == this.Document, "Child is in different document");
}
if (this.IsCached)
Assert(child.IsCached, "Child not in cache");
Assert(myStartOffset <= child.StartOffset && child.EndOffset <= myEndOffset, "Child not within parent text range");
if (prevChild != null)
Assert(prevChild.EndOffset <= child.StartOffset, "Overlaping childs");
child.DebugCheckConsistency(checkParentPointers);
prevChild = child;
}
}
/// <remarks>
/// Note the the method is not called recuively.
/// Only the helper methods are recursive.
/// </remarks>
internal void UpdateTreeFrom(AXmlContainer srcContainer)
{
this.StartOffset = srcContainer.StartOffset; // Force the update
this.UpdateDataFrom(srcContainer);
RemoveChildrenNotIn(srcContainer.Children);
InsertAndUpdateChildrenFrom(srcContainer.Children);
}
void RemoveChildrenNotIn(IList<AXmlObject> srcList)
{
Dictionary<int, AXmlObject> srcChildren = srcList.ToDictionary(i => i.StartOffset);
for(int i = 0; i < this.Children.Count;) {
AXmlObject child = this.Children[i];
AXmlObject srcChild;
if (srcChildren.TryGetValue(child.StartOffset, out srcChild) && child.CanUpdateDataFrom(srcChild)) {
// Keep only one item with given offset (we might have several due to deletion)
srcChildren.Remove(child.StartOffset);
// If contaner that needs updating
AXmlContainer childAsContainer = child as AXmlContainer;
if (childAsContainer != null && child.LastUpdatedFrom != srcChild)
childAsContainer.RemoveChildrenNotIn(((AXmlContainer)srcChild).Children);
i++;
} else {
RemoveChild(i);
}
}
}
void InsertAndUpdateChildrenFrom(IList<AXmlObject> srcList)
{
for(int i = 0; i < srcList.Count; i++) {
// End of our list?
if (i == this.Children.Count) {
InsertChild(i, srcList[i]);
continue;
}
AXmlObject child = this.Children[i];
AXmlObject srcChild = srcList[i];
if (child.CanUpdateDataFrom(srcChild)) { // includes offset test
// Does it need updating?
if (child.LastUpdatedFrom != srcChild) {
child.UpdateDataFrom(srcChild);
AXmlContainer childAsContainer = child as AXmlContainer;
if (childAsContainer != null)
childAsContainer.InsertAndUpdateChildrenFrom(((AXmlContainer)srcChild).Children);
}
} else {
InsertChild(i, srcChild);
}
}
Assert(this.Children.Count == srcList.Count, "List lengths differ after update");
}
}
}

69
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs

@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// The root object of the XML document
/// </summary>
public class AXmlDocument: AXmlContainer
{
/// <summary> Parser that produced this document </summary>
internal AXmlParser Parser { get; set; }
/// <summary> Occurs when object is added to any part of the document </summary>
public event EventHandler<NotifyCollectionChangedEventArgs> ObjectInserted;
/// <summary> Occurs when object is removed from any part of the document </summary>
public event EventHandler<NotifyCollectionChangedEventArgs> ObjectRemoved;
/// <summary> Occurs before local data of any object in the document changes </summary>
public event EventHandler<AXmlObjectEventArgs> ObjectChanging;
/// <summary> Occurs after local data of any object in the document changed </summary>
public event EventHandler<AXmlObjectEventArgs> ObjectChanged;
internal void OnObjectInserted(int index, AXmlObject obj)
{
if (ObjectInserted != null)
ObjectInserted(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new AXmlObject[] { obj }.ToList(), index));
}
internal void OnObjectRemoved(int index, AXmlObject obj)
{
if (ObjectRemoved != null)
ObjectRemoved(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new AXmlObject[] { obj }.ToList(), index));
}
internal void OnObjectChanging(AXmlObject obj)
{
if (ObjectChanging != null)
ObjectChanging(this, new AXmlObjectEventArgs() { Object = obj } );
}
internal void OnObjectChanged(AXmlObject obj)
{
if (ObjectChanged != null)
ObjectChanged(this, new AXmlObjectEventArgs() { Object = obj } );
}
/// <inheritdoc/>
public override void AcceptVisitor(IAXmlVisitor visitor)
{
visitor.VisitDocument(this);
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} Chld:{1}]", base.ToString(), this.Children.Count);
}
}
}

226
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs

@ -1,226 +0,0 @@ @@ -1,226 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Logical grouping of other nodes together.
/// </summary>
public class AXmlElement: AXmlContainer
{
/// <summary> No tags are missing anywhere within this element (recursive) </summary>
public bool IsProperlyNested { get; set; }
/// <returns> True in wellformed XML </returns>
public bool HasStartOrEmptyTag { get; set; }
/// <returns> True in wellformed XML </returns>
public bool HasEndTag { get; set; }
/// <inheritdoc/>
internal override bool UpdateDataFrom(AXmlObject source)
{
if (!base.UpdateDataFrom(source)) return false;
AXmlElement src = (AXmlElement)source;
// Clear the cache for this - quite expensive
attributesAndElements = null;
if (this.IsProperlyNested != src.IsProperlyNested ||
this.HasStartOrEmptyTag != src.HasStartOrEmptyTag ||
this.HasEndTag != src.HasEndTag)
{
OnChanging();
this.IsProperlyNested = src.IsProperlyNested;
this.HasStartOrEmptyTag = src.HasStartOrEmptyTag;
this.HasEndTag = src.HasEndTag;
OnChanged();
return true;
} else {
return false;
}
}
/// <summary> The start or empty-element tag if there is any </summary>
internal AXmlTag StartTag {
get {
Assert(HasStartOrEmptyTag, "Does not have a start tag");
return (AXmlTag)this.Children[0];
}
}
/// <summary> The end tag if there is any </summary>
internal AXmlTag EndTag {
get {
Assert(HasEndTag, "Does not have an end tag");
return (AXmlTag)this.Children[this.Children.Count - 1];
}
}
internal override void DebugCheckConsistency(bool checkParentPointers)
{
DebugAssert(Children.Count > 0, "No children");
base.DebugCheckConsistency(checkParentPointers);
}
#region Helpper methods
/// <summary> Gets attributes of the element </summary>
/// <remarks>
/// Warning: this is a cenvenience method to access the attributes of the start tag.
/// However, since the start tag might be moved/replaced, this property might return
/// different values over time.
/// </remarks>
public AXmlAttributeCollection Attributes {
get {
if (this.HasStartOrEmptyTag) {
return this.StartTag.Attributes;
} else {
return AXmlAttributeCollection.Empty;
}
}
}
ObservableCollection<AXmlObject> attributesAndElements;
/// <summary> Gets both attributes and elements. Expensive, avoid use. </summary>
/// <remarks> Warning: the collection will regenerate after each update </remarks>
public ObservableCollection<AXmlObject> AttributesAndElements {
get {
if (attributesAndElements == null) {
if (this.HasStartOrEmptyTag) {
attributesAndElements = new MergedCollection<AXmlObject, ObservableCollection<AXmlObject>> (
// New wrapper with RawObject types
new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.StartTag.Children, x => x is AXmlAttribute),
new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement)
);
} else {
attributesAndElements = new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement);
}
}
return attributesAndElements;
}
}
/// <summary> Name with namespace prefix - exactly as in source </summary>
public string Name {
get {
if (this.HasStartOrEmptyTag) {
return this.StartTag.Name;
} else {
return this.EndTag.Name;
}
}
}
/// <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> Empty string if not found </returns>
public string LocalName {
get {
return GetLocalName(this.Name);
}
}
/// <summary> Resolved namespace of the name </summary>
/// <returns> Empty string if prefix is not found </returns>
public string Namespace {
get {
string prefix = this.Prefix;
if (string.IsNullOrEmpty(prefix)) {
return FindDefaultNamespace();
} else {
return ResolvePrefix(prefix);
}
}
}
/// <summary> Find the defualt namespace for this context </summary>
public string FindDefaultNamespace()
{
AXmlElement current = this;
while(current != null) {
string namesapce = current.GetAttributeValue(NoNamespace, "xmlns");
if (namesapce != null) return namesapce;
current = current.Parent as AXmlElement;
}
return string.Empty; // No namesapce
}
/// <summary>
/// Recursively resolve given prefix in this context. Prefix must have some value.
/// </summary>
/// <returns> Empty string if prefix is not found </returns>
public string ResolvePrefix(string prefix)
{
if (string.IsNullOrEmpty(prefix)) throw new ArgumentException("No prefix given", "prefix");
// Implicit namesapces
if (prefix == "xml") return XmlNamespace;
if (prefix == "xmlns") return XmlnsNamespace;
AXmlElement current = this;
while(current != null) {
string namesapce = current.GetAttributeValue(XmlnsNamespace, prefix);
if (namesapce != null) return namesapce;
current = current.Parent as AXmlElement;
}
return NoNamespace; // Can not find prefix
}
/// <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(NoNamespace, 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.GetByLocalName(localName)) {
DebugAssert(attr.LocalName == localName, "Bad hashtable");
if (attr.Namespace == @namespace) {
return attr.Value;
}
}
return null;
}
#endregion
/// <inheritdoc/>
public override void AcceptVisitor(IAXmlVisitor 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.HasStartOrEmptyTag ? this.StartTag.Children.Count : 0, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad");
}
}
}

266
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs

@ -1,266 +0,0 @@ @@ -1,266 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Abstact base class for all types
/// </summary>
public abstract class AXmlObject: TextSegment
{
/// <summary> Empty string. The namespace used if there is no "xmlns" specified </summary>
public 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/";
/// <summary> Parent node. </summary>
/// <remarks>
/// New cached items start with null parent.
/// Cache constraint:
/// If cached item has parent set, then the whole subtree must be consistent
/// </remarks>
public AXmlObject Parent { get; set; }
/// <summary>
/// Gets the document that has owns this object.
/// Once set, it is not changed. Not even set to null.
/// </summary>
internal AXmlDocument Document { get; set; }
/// <summary> Creates new object </summary>
protected AXmlObject()
{
this.LastUpdatedFrom = this;
}
/// <summary> Occurs before the value of any local properties changes. Nested changes do not cause the event to occur </summary>
public event EventHandler<AXmlObjectEventArgs> Changing;
/// <summary> Occurs after the value of any local properties changed. Nested changes do not cause the event to occur </summary>
public event EventHandler<AXmlObjectEventArgs> Changed;
/// <summary> Raises Changing event </summary>
protected void OnChanging()
{
AXmlParser.Log("Changing {0}", this);
if (Changing != null) {
Changing(this, new AXmlObjectEventArgs() { Object = this } );
}
AXmlDocument doc = this.Document;
if (doc != null) {
doc.OnObjectChanging(this);
}
// As a convenience, also rasie an event for the parent element
AXmlTag me = this as AXmlTag;
if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) {
me.Parent.OnChanging();
}
}
/// <summary> Raises Changed event </summary>
protected void OnChanged()
{
AXmlParser.Log("Changed {0}", this);
if (Changed != null) {
Changed(this, new AXmlObjectEventArgs() { Object = this } );
}
AXmlDocument doc = this.Document;
if (doc != null) {
doc.OnObjectChanged(this);
}
// As a convenience, also rasie an event for the parent element
AXmlTag me = this as AXmlTag;
if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) {
me.Parent.OnChanged();
}
}
List<SyntaxError> syntaxErrors;
/// <summary>
/// The error that occured in the context of this node (excluding nested nodes)
/// </summary>
public IEnumerable<SyntaxError> MySyntaxErrors {
get {
if (syntaxErrors == null) {
return new SyntaxError[] {};
} else {
return syntaxErrors;
}
}
}
/// <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 GetSelfAndAllChildren().SelectMany(obj => obj.MySyntaxErrors);
}
}
internal void AddSyntaxError(SyntaxError error)
{
DebugAssert(error.Object == this, "Must own the error");
if (this.syntaxErrors == null) this.syntaxErrors = new List<SyntaxError>();
syntaxErrors.Add(error);
}
/// <summary> Throws exception if condition is false </summary>
/// <remarks> Present in release mode - use only for very cheap aserts </remarks>
protected 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")]
protected static void DebugAssert(bool condition, string message)
{
if (!condition) {
throw new InternalException("Assertion failed: " + message);
}
}
/// <summary> Recursively gets self and all nested nodes. </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")]
public virtual IEnumerable<AXmlObject> GetSelfAndAllChildren()
{
return new AXmlObject[] { this };
}
/// <summary> Get all ancestors of this node </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")]
public IEnumerable<AXmlObject> GetAncestors()
{
AXmlObject curr = this.Parent;
while(curr != null) {
yield return curr;
curr = curr.Parent;
}
}
/// <summary> Call appropriate visit method on the given visitor </summary>
public abstract void AcceptVisitor(IAXmlVisitor visitor);
/// <summary> The parser tree object this object was updated from </summary>
/// <remarks> Initialized to 'this' </remarks>
internal AXmlObject LastUpdatedFrom { get; private set; }
internal bool IsCached { get; set; }
/// <summary> Is call to UpdateDataFrom is allowed? </summary>
internal bool CanUpdateDataFrom(AXmlObject source)
{
return
this.GetType() == source.GetType() &&
this.StartOffset == source.StartOffset &&
(this.LastUpdatedFrom == source || !this.IsCached);
}
/// <summary> Copy all data from the 'source' to this object </summary>
/// <remarks> Returns true if any updates were done </remarks>
internal virtual bool UpdateDataFrom(AXmlObject source)
{
Assert(this.GetType() == source.GetType(), "Source has different type");
DebugAssert(this.StartOffset == source.StartOffset, "Source has different StartOffset");
if (this.LastUpdatedFrom == source) {
DebugAssert(this.EndOffset == source.EndOffset, "Source has different EndOffset");
return false;
}
Assert(!this.IsCached, "Can not update cached item");
Assert(source.IsCached, "Must update from cache");
this.LastUpdatedFrom = source;
this.StartOffset = source.StartOffset;
// In some cases we are just updating objects of that same
// type and position and hoping to be luckily right
this.EndOffset = source.EndOffset;
// Do not bother comparing - assume changed if non-null
if (this.syntaxErrors != null || source.syntaxErrors != null) {
// May be called again in derived class - oh, well, does not matter
OnChanging();
this.Document.Parser.TrackedSegments.RemoveSyntaxErrorsOf(this);
if (source.syntaxErrors == null) {
this.syntaxErrors = null;
} else {
this.syntaxErrors = new List<SyntaxError>();
foreach(var error in source.MySyntaxErrors) {
// The object differs, so create our own copy
// The source still might need it in the future and we do not want to break it
this.AddSyntaxError(error.Clone(this));
}
}
this.Document.Parser.TrackedSegments.AddSyntaxErrorsOf(this);
OnChanged();
}
return true;
}
/// <summary> Verify that the item is consistent. Only in debug build. </summary>
[Conditional("DEBUG")]
internal virtual void DebugCheckConsistency(bool allowNullParent)
{
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}({1}-{2})", this.GetType().Name.Remove(0, 4), this.StartOffset, this.EndOffset);
}
#region Helpper methods
/// <summary> The part of name before ":" </summary>
/// <returns> Empty string if not found </returns>
protected 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>
protected 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
}
}

90
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObjectCollection.cs

@ -1,90 +0,0 @@ @@ -1,90 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Collection that is publicly read-only and has support
/// for adding/removing multiple items at a time.
/// </summary>
public class AXmlObjectCollection<T>: Collection<T>, INotifyCollectionChanged
{
/// <summary> Occurs when the collection is changed </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary> Raises <see cref="CollectionChanged"/> event </summary>
// Do not inherit - it is not called if event is null
void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null) {
CollectionChanged(this, e);
}
}
/// <inheritdoc/>
protected override void ClearItems()
{
throw new NotSupportedException();
}
/// <inheritdoc/>
protected override void InsertItem(int index, T item)
{
throw new NotSupportedException();
}
/// <inheritdoc/>
protected override void RemoveItem(int index)
{
throw new NotSupportedException();
}
/// <inheritdoc/>
protected override void SetItem(int index, T item)
{
throw new NotSupportedException();
}
internal void InsertItemAt(int index, T item)
{
base.InsertItem(index, item);
if (CollectionChanged != null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new T[] { item }.ToList(), index));
}
internal void RemoveItemAt(int index)
{
T removed = this[index];
base.RemoveItem(index);
if (CollectionChanged != null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new T[] { removed }.ToList(), index));
}
internal void InsertItemsAt(int index, IList<T> items)
{
for(int i = 0; i < items.Count; i++) {
base.InsertItem(index + i, items[i]);
}
if (CollectionChanged != null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (IList)items, index));
}
internal void RemoveItemsAt(int index, int count)
{
List<T> removed = new List<T>();
for(int i = 0; i < count; i++) {
removed.Add(this[index]);
base.RemoveItem(index);
}
if (CollectionChanged != null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)removed, index));
}
}
}

21
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObjectEventArgs.cs

@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary> Holds event args for event caused by <see cref="AXmlObject"/> </summary>
public class AXmlObjectEventArgs: EventArgs
{
/// <summary> The object that caused the event </summary>
public AXmlObject Object { get; set; }
}
}

201
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs

@ -1,201 +0,0 @@ @@ -1,201 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Creates object tree from XML document.
/// </summary>
/// <remarks>
/// The created tree fully describes the document and thus the orginal XML file can be
/// exactly reproduced.
///
/// Any further parses will reparse only the changed parts and the existing tree will
/// be updated with the changes. The user can add event handlers to be notified of
/// the changes. The parser tries to minimize the number of changes to the tree.
/// (for example, it will add a single child at the start of collection rather than
/// clearing the collection and adding new children)
///
/// The object tree consists of following types:
/// RawObject - Abstact base class for all types
/// RawContainer - Abstact base class for all types that can contain child nodes
/// RawDocument - The root object of the XML document
/// RawElement - Logical grouping of other nodes together. The first child is always the start tag.
/// RawTag - Represents any markup starting with "&lt;" and (hopefully) ending with ">"
/// RawAttribute - Name-value pair in a tag
/// RawText - Whitespace or character data
///
/// For example, see the following XML and the produced object tree:
/// <![CDATA[
/// <!-- My favourite quote -->
/// <quote author="Albert Einstein">
/// Make everything as simple as possible, but not simpler.
/// </quote>
///
/// RawDocument
/// RawTag "<!--" "-->"
/// RawText " My favourite quote "
/// RawElement
/// RawTag "<" "quote" ">"
/// RawText " "
/// RawAttribute 'author="Albert Einstein"'
/// RawText "\n Make everything as simple as possible, but not simpler.\n"
/// RawTag "</" "quote" ">"
/// ]]>
///
/// The precise content of RawTag depends on what it represents:
/// <![CDATA[
/// Start tag: "<" Name? (RawText+ RawAttribute)* RawText* (">" | "/>")
/// End tag: "</" Name? (RawText+ RawAttribute)* RawText* ">"
/// P.instr.: "<?" Name? (RawText)* "?>"
/// Comment: "<!--" (RawText)* "-->"
/// CData: "<![CDATA[" (RawText)* "]]" ">"
/// DTD: "<!DOCTYPE" (RawText+ RawTag)* RawText* ">" (DOCTYPE or other DTD names)
/// UknownBang: "<!" (RawText)* ">"
/// ]]>
///
/// The type of tag can be identified by the opening backet.
/// There are helpper properties in the RawTag class to identify the type, exactly
/// one of the properties will be true.
///
/// The closing bracket may be missing or may be different for mallformed XML.
///
/// Note that there can always be multiple consequtive RawText nodes.
/// This is to ensure that idividual texts are not too long.
///
/// XML Spec: http://www.w3.org/TR/xml/
/// XML EBNF: http://www.jelks.nu/XML/xmlebnf.html
///
/// Internals:
///
/// "Try" methods can silently fail by returning false.
/// MoveTo methods do not move if they are already at the given target
/// If methods return some object, it must be no-empty. It is up to the caller to ensure
/// the context is appropriate for reading.
///
/// </remarks>
public class AXmlParser
{
AXmlDocument userDocument;
internal TrackedSegmentCollection TrackedSegments { get; private set; }
/// <summary>
/// Generate syntax error when seeing enity reference other then the build-in ones
/// </summary>
public bool UnknownEntityReferenceIsError { get; set; }
/// <summary> Create new parser </summary>
public AXmlParser()
{
this.Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
ClearInternal();
}
/// <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 Log(string text, params object[] pars)
{
//System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "XML: " + text, pars));
}
/// <summary>
/// Incrementaly parse the given text.
/// You have to hold the write lock.
/// </summary>
/// <param name="input">
/// The full XML text of the new document.
/// </param>
/// <param name="changesSinceLastParse">
/// Changes since last parse. Null will cause full reparse.
/// </param>
public AXmlDocument Parse(string input, IEnumerable<DocumentChangeEventArgs> changesSinceLastParse)
{
if (!Lock.IsWriteLockHeld)
throw new InvalidOperationException("Lock needed!");
// Use changes to invalidate cache
if (changesSinceLastParse != null) {
this.TrackedSegments.UpdateOffsetsAndInvalidate(changesSinceLastParse);
} else {
this.TrackedSegments.InvalidateAll();
}
TagReader tagReader = new TagReader(this, input);
List<AXmlObject> tags = tagReader.ReadAllTags();
AXmlDocument parsedDocument = new TagMatchingHeuristics(this, input, tags).ReadDocument();
tagReader.PrintStringCacheStats();
AXmlParser.Log("Updating main DOM tree...");
userDocument.UpdateTreeFrom(parsedDocument);
userDocument.DebugCheckConsistency(true);
Assert(userDocument.GetSelfAndAllChildren().Count() == parsedDocument.GetSelfAndAllChildren().Count(), "Parsed document and updated document have different number of children");
return userDocument;
}
/// <summary>
/// Makes calls to Parse() thread-safe. Use Lock everywhere Parse() is called.
/// </summary>
public ReaderWriterLockSlim Lock { get; private set; }
/// <summary>
/// Returns the last cached version of the document.
/// </summary>
/// <exception cref="InvalidOperationException">No read lock is held by the current thread.</exception>
public AXmlDocument LastDocument {
get {
if (!Lock.IsReadLockHeld)
throw new InvalidOperationException("Read lock needed!");
return userDocument;
}
}
/// <summary>
/// Clears the parser data.
/// </summary>
/// <exception cref="InvalidOperationException">No write lock is held by the current thread.</exception>
public void Clear()
{
if (!Lock.IsWriteLockHeld)
throw new InvalidOperationException("Write lock needed!");
ClearInternal();
}
void ClearInternal()
{
this.UnknownEntityReferenceIsError = true;
this.TrackedSegments = new TrackedSegmentCollection();
this.userDocument = new AXmlDocument() { Parser = this };
this.userDocument.Document = this.userDocument;
// Track the document
this.TrackedSegments.AddParsedObject(this.userDocument, null);
this.userDocument.IsCached = false;
}
}
}

108
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs

@ -1,108 +0,0 @@ @@ -1,108 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Represents any markup starting with "&lt;" and (hopefully) ending with ">"
/// </summary>
public class AXmlTag: AXmlContainer
{
/// <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" } );
/// <summary> Opening bracket - usually "&lt;" </summary>
public string OpeningBracket { get; internal set; }
/// <summary> Name following the opening bracket </summary>
public string Name { get; internal set; }
/// <summary> Opening bracket - usually "&gt;" </summary>
public string ClosingBracket { get; internal set; }
/// <summary> True if tag starts with "&lt;" </summary>
public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } }
/// <summary> True if tag starts with "&lt;" and ends with "&gt;" </summary>
public bool IsStartTag { get { return OpeningBracket == "<" && ClosingBracket == ">"; } }
/// <summary> True if tag starts with "&lt;" and does not end with "&gt;" </summary>
public bool IsEmptyTag { get { return OpeningBracket == "<" && ClosingBracket != ">" ; } }
/// <summary> True if tag starts with "&lt;/" </summary>
public bool IsEndTag { get { return OpeningBracket == "</"; } }
/// <summary> True if tag starts with "&lt;?" </summary>
public bool IsProcessingInstruction { get { return OpeningBracket == "<?"; } }
/// <summary> True if tag starts with "&lt;!--" </summary>
public bool IsComment { get { return OpeningBracket == "<!--"; } }
/// <summary> True if tag starts with "&lt;![CDATA[" </summary>
public bool IsCData { get { return OpeningBracket == "<![CDATA["; } }
/// <summary> True if tag starts with one of the DTD starts </summary>
public bool IsDocumentType { get { return DtdNames.Contains(OpeningBracket); } }
/// <summary> True if tag starts with "&lt;!" </summary>
public bool IsUnknownBang { get { return OpeningBracket == "<!"; } }
#region Helpper methods
AXmlAttributeCollection attributes;
/// <summary> Gets attributes of the tag (if applicable) </summary>
public AXmlAttributeCollection Attributes {
get {
if (attributes == null) {
attributes = new AXmlAttributeCollection(this.Children);
}
return attributes;
}
}
#endregion
internal override void DebugCheckConsistency(bool checkParentPointers)
{
Assert(OpeningBracket != null, "Null OpeningBracket");
Assert(Name != null, "Null Name");
Assert(ClosingBracket != null, "Null ClosingBracket");
base.DebugCheckConsistency(checkParentPointers);
}
/// <inheritdoc/>
public override void AcceptVisitor(IAXmlVisitor visitor)
{
visitor.VisitTag(this);
}
/// <inheritdoc/>
internal override bool UpdateDataFrom(AXmlObject source)
{
if (!base.UpdateDataFrom(source)) return false;
AXmlTag src = (AXmlTag)source;
if (this.OpeningBracket != src.OpeningBracket ||
this.Name != src.Name ||
this.ClosingBracket != src.ClosingBracket)
{
OnChanging();
this.OpeningBracket = src.OpeningBracket;
this.Name = src.Name;
this.ClosingBracket = src.ClosingBracket;
OnChanged();
return true;
} else {
return false;
}
}
/// <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);
}
}
}

62
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Whitespace or character data
/// </summary>
public class AXmlText: AXmlObject
{
/// <summary> The context in which the text occured </summary>
internal TextType Type { get; set; }
/// <summary> The text exactly as in source </summary>
public string EscapedValue { get; set; }
/// <summary> The text with all entity references resloved </summary>
public string Value { get; set; }
/// <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; set; }
/// <inheritdoc/>
public override void AcceptVisitor(IAXmlVisitor visitor)
{
visitor.VisitText(this);
}
/// <inheritdoc/>
internal override bool UpdateDataFrom(AXmlObject source)
{
if (!base.UpdateDataFrom(source)) return false;
AXmlText src = (AXmlText)source;
if (this.EscapedValue != src.EscapedValue ||
this.Value != src.Value)
{
OnChanging();
this.EscapedValue = src.EscapedValue;
this.Value = src.Value;
OnChanged();
return true;
} else {
return false;
}
}
/// <inheritdoc/>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[{0} Text.Length={1}]", base.ToString(), this.EscapedValue.Length);
}
}
}

44
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AbstractAXmlVisitor.cs

@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Text;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Derive from this class to create visitor for the XML tree
/// </summary>
public abstract class AbstractAXmlVisitor : IAXmlVisitor
{
/// <summary> Visit RawDocument </summary>
public virtual void VisitDocument(AXmlDocument document)
{
foreach(AXmlObject child in document.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawElement </summary>
public virtual void VisitElement(AXmlElement element)
{
foreach(AXmlObject child in element.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawTag </summary>
public virtual void VisitTag(AXmlTag tag)
{
foreach(AXmlObject child in tag.Children) child.AcceptVisitor(this);
}
/// <summary> Visit RawAttribute </summary>
public virtual void VisitAttribute(AXmlAttribute attribute)
{
}
/// <summary> Visit RawText </summary>
public virtual void VisitText(AXmlText text)
{
}
}
}

119
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs

@ -1,119 +0,0 @@ @@ -1,119 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Linq;
using System.Text;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Converts the XML tree back to text in canonical form.
/// See http://www.w3.org/TR/xml-c14n
/// </summary>
public class CanonicalPrintAXmlVisitor: AbstractAXmlVisitor
{
StringBuilder sb = new StringBuilder();
/// <summary>
/// Gets the pretty printed text
/// </summary>
public string Output {
get {
return sb.ToString();
}
}
/// <summary> Create canonical text from a document </summary>
public static string Print(AXmlDocument doc)
{
CanonicalPrintAXmlVisitor visitor = new CanonicalPrintAXmlVisitor();
visitor.VisitDocument(doc);
return visitor.Output;
}
/// <summary> Visit RawDocument </summary>
public override void VisitDocument(AXmlDocument document)
{
foreach(AXmlObject child in document.Children) {
AXmlTag childAsTag = child as AXmlTag;
// Only procssing instructions or elements
if (childAsTag != null && childAsTag.IsProcessingInstruction && childAsTag.Name != "xml") {
VisitTag(childAsTag);
} else {
AXmlElement childAsElement = child as AXmlElement;
if (childAsElement != null) {
VisitElement(childAsElement);
}
}
}
}
/// <summary> Visit RawElement </summary>
public override void VisitElement(AXmlElement element)
{
base.VisitElement(element);
}
/// <summary> Visit RawTag </summary>
public override void VisitTag(AXmlTag tag)
{
if (tag.IsStartOrEmptyTag) {
sb.Append('<');
sb.Append(tag.Name);
foreach(AXmlAttribute attr in tag.Children.OfType<AXmlAttribute>().OrderBy(a => a.Name)) {
VisitAttribute(attr);
}
sb.Append('>');
if (tag.IsEmptyTag) {
// Use explicit start-end pair
sb.AppendFormat("</{0}>", tag.Name);
}
} else if (tag.IsEndTag) {
sb.AppendFormat("</{0}>", tag.Name);
} else if (tag.IsProcessingInstruction) {
sb.Append("<?");
sb.Append(tag.Name);
foreach(AXmlText text in tag.Children.OfType<AXmlText>()) {
sb.Append(text.Value);
}
if (tag.Children.Count == 0)
sb.Append(' ');
sb.Append("?>");
} else if (tag.IsCData) {
foreach(AXmlText text in tag.Children.OfType<AXmlText>()) {
sb.Append(Escape(text.Value));
}
}
}
/// <summary> Visit RawAttribute </summary>
public override void VisitAttribute(AXmlAttribute attribute)
{
sb.Append(' ');
sb.Append(attribute.Name);
sb.Append("=");
sb.Append('"');
sb.Append(Escape(attribute.Value));
sb.Append('"');
}
/// <summary> Visit RawText </summary>
public override void VisitText(AXmlText text)
{
sb.Append(Escape(text.Value));
}
static string Escape(string text)
{
return text
.Replace("&", "&amp;")
.Replace("<", "&lt;")
.Replace(">", "&gt;")
.Replace("\"", "&quot;")
.Replace("\u0009", "&#9;")
.Replace("\u000A", "&#10;")
.Replace("\u000D", "&#13;");
}
}
}

99
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/FilteredCollection.cs

@ -1,99 +0,0 @@ @@ -1,99 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Collection that presents only some items from the wrapped collection.
/// It implicitely filters object that are not of type T (or derived).
/// </summary>
public class FilteredCollection<T, TCollection>: ObservableCollection<T> where TCollection : INotifyCollectionChanged, IList
{
TCollection source;
Predicate<object> condition;
List<int> srcPtrs = new List<int>(); // Index to the original collection
/// <summary> Create unbound collection </summary>
protected FilteredCollection() {}
/// <summary> Wrap the given collection. Items of type other then T are filtered </summary>
public FilteredCollection(TCollection source) : this (source, x => true) { }
/// <summary> Wrap the given collection. Items of type other then T are filtered. Items not matching the condition are filtered. </summary>
public FilteredCollection(TCollection source, Predicate<object> condition)
{
this.source = source;
this.condition = condition;
this.source.CollectionChanged += SourceCollectionChanged;
Reset();
}
void Reset()
{
this.Clear();
srcPtrs.Clear();
for(int i = 0; i < source.Count; i++) {
if (source[i] is T && condition(source[i])) {
this.Add((T)source[i]);
srcPtrs.Add(i);
}
}
}
void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch(e.Action) {
case NotifyCollectionChangedAction.Add:
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] >= e.NewStartingIndex) {
srcPtrs[i] += e.NewItems.Count;
}
}
// Find where to add items
int addIndex = srcPtrs.FindIndex(srcPtr => srcPtr >= e.NewStartingIndex);
if (addIndex == -1) addIndex = this.Count;
// Add items to collection
for(int i = 0; i < e.NewItems.Count; i++) {
if (e.NewItems[i] is T && condition(e.NewItems[i])) {
this.InsertItem(addIndex, (T)e.NewItems[i]);
srcPtrs.Insert(addIndex, e.NewStartingIndex + i);
addIndex++;
}
}
break;
case NotifyCollectionChangedAction.Remove:
// Remove the item from our collection
for(int i = 0; i < e.OldItems.Count; i++) {
// Anyone points to the removed item?
int removeIndex = srcPtrs.IndexOf(e.OldStartingIndex + i);
// Remove
if (removeIndex != -1) {
this.RemoveAt(removeIndex);
srcPtrs.RemoveAt(removeIndex);
}
}
// Update pointers
for(int i = 0; i < srcPtrs.Count; i++) {
if (srcPtrs[i] >= e.OldStartingIndex) {
srcPtrs[i] -= e.OldItems.Count;
}
}
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
default:
throw new NotSupportedException(e.Action.ToString());
}
}
}
}

29
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/IAXmlVisitor.cs

@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Text;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Visitor for the XML tree
/// </summary>
public interface IAXmlVisitor
{
/// <summary> Visit RawDocument </summary>
void VisitDocument(AXmlDocument document);
/// <summary> Visit RawElement </summary>
void VisitElement(AXmlElement element);
/// <summary> Visit RawTag </summary>
void VisitTag(AXmlTag tag);
/// <summary> Visit RawAttribute </summary>
void VisitAttribute(AXmlAttribute attribute);
/// <summary> Visit RawText </summary>
void VisitText(AXmlText text);
}
}

45
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs

@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Exception used for internal errors in XML parser.
/// This exception indicates a bug in AvalonEdit.
/// </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)
{
}
}
}

70
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/MergedCollection.cs

@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Two collections in sequence
/// </summary>
public class MergedCollection<T, TCollection> : ObservableCollection<T> where TCollection : INotifyCollectionChanged, IList<T>
{
TCollection a;
TCollection b;
/// <summary> Create a wrapper containing elements of 'a' and then 'b' </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public MergedCollection(TCollection a, TCollection b)
{
this.a = a;
this.b = b;
this.a.CollectionChanged += SourceCollectionAChanged;
this.b.CollectionChanged += SourceCollectionBChanged;
Reset();
}
void Reset()
{
this.Clear();
foreach(T item in a) this.Add(item);
foreach(T item in b) this.Add(item);
}
void SourceCollectionAChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SourceCollectionChanged(0, e);
}
void SourceCollectionBChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SourceCollectionChanged(a.Count, e);
}
void SourceCollectionChanged(int collectionStart, NotifyCollectionChangedEventArgs e)
{
switch(e.Action) {
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; i++) {
this.InsertItem(collectionStart + e.NewStartingIndex + i, (T)e.NewItems[i]);
}
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; i++) {
this.RemoveAt(collectionStart + e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Reset:
Reset();
break;
default:
throw new NotSupportedException(e.Action.ToString());
}
}
}
}

69
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs

@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Text;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Converts the XML tree back to text.
/// The text should exactly match the original.
/// </summary>
public class PrettyPrintAXmlVisitor: AbstractAXmlVisitor
{
StringBuilder sb = new StringBuilder();
/// <summary>
/// Gets the pretty printed text
/// </summary>
public string Output {
get {
return sb.ToString();
}
}
/// <summary> Create XML text from a document </summary>
public static string PrettyPrint(AXmlDocument doc)
{
PrettyPrintAXmlVisitor visitor = new PrettyPrintAXmlVisitor();
visitor.VisitDocument(doc);
return visitor.Output;
}
/// <summary> Visit RawDocument </summary>
public override void VisitDocument(AXmlDocument document)
{
base.VisitDocument(document);
}
/// <summary> Visit RawElement </summary>
public override void VisitElement(AXmlElement element)
{
base.VisitElement(element);
}
/// <summary> Visit RawTag </summary>
public override void VisitTag(AXmlTag tag)
{
sb.Append(tag.OpeningBracket);
sb.Append(tag.Name);
base.VisitTag(tag);
sb.Append(tag.ClosingBracket);
}
/// <summary> Visit RawAttribute </summary>
public override void VisitAttribute(AXmlAttribute attribute)
{
sb.Append(attribute.Name);
sb.Append(attribute.EqualsSign);
sb.Append(attribute.QuotedValue);
}
/// <summary> Visit RawText </summary>
public override void VisitText(AXmlText text)
{
sb.Append(text.EscapedValue);
}
}
}

36
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/SyntaxError.cs

@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary> Information about syntax error that occured during parsing </summary>
public class SyntaxError: TextSegment
{
/// <summary> Object for which the error occured </summary>
public AXmlObject Object { get; internal set; }
/// <summary> Textual description of the error </summary>
public string Message { get; internal set; }
/// <summary> Any user data </summary>
public object Tag { get; set; }
internal SyntaxError Clone(AXmlObject newOwner)
{
return new SyntaxError {
Object = newOwner,
Message = Message,
Tag = Tag,
StartOffset = StartOffset,
EndOffset = EndOffset,
};
}
}
}

740
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs

@ -1,740 +0,0 @@ @@ -1,740 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace ICSharpCode.AvalonEdit.Xml
{
class TagReader: TokenReader
{
AXmlParser parser;
TrackedSegmentCollection trackedSegments;
string input;
public TagReader(AXmlParser parser, string input): base(input)
{
this.parser = parser;
this.trackedSegments = parser.TrackedSegments;
this.input = input;
}
bool TryReadFromCacheOrNew<T>(out T res) where T: AXmlObject, new()
{
return TryReadFromCacheOrNew(out res, t => true);
}
bool TryReadFromCacheOrNew<T>(out T res, Predicate<T> condition) where T: AXmlObject, new()
{
T cached = trackedSegments.GetCachedObject<T>(this.CurrentLocation, 0, condition);
if (cached != null) {
Skip(cached.Length);
AXmlParser.Assert(cached.Length > 0, "cached elements must not have zero length");
res = cached;
return true;
} else {
res = new T();
return false;
}
}
void OnParsed(AXmlObject obj)
{
AXmlParser.Log("Parsed {0}", obj);
trackedSegments.AddParsedObject(obj, this.MaxTouchedLocation > this.CurrentLocation ? (int?)this.MaxTouchedLocation : null);
}
/// <summary>
/// Read all tags in the document in a flat sequence.
/// It also includes the text between tags and possibly some properly nested Elements from cache.
/// </summary>
public List<AXmlObject> ReadAllTags()
{
List<AXmlObject> stream = new List<AXmlObject>();
while(true) {
if (IsEndOfFile()) {
break;
} else if (TryPeek('<')) {
AXmlElement elem;
if (TryReadFromCacheOrNew(out elem, e => e.IsProperlyNested)) {
stream.Add(elem);
} else {
stream.Add(ReadTag());
}
} else {
stream.AddRange(ReadText(TextType.CharacterData));
}
}
return stream;
}
/// <summary>
/// Context: "&lt;"
/// </summary>
AXmlTag ReadTag()
{
AssertHasMoreData();
AXmlTag tag;
if (TryReadFromCacheOrNew(out tag)) return tag;
tag.StartOffset = this.CurrentLocation;
// 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(tag, tag.StartOffset, this.CurrentLocation, "Unknown tag");
if (tag.IsStartOrEmptyTag || tag.IsEndTag || tag.IsProcessingInstruction) {
// Read the name
string name;
if (TryReadName(out name)) {
if (!IsValidName(name)) {
OnSyntaxError(tag, this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
}
} else {
OnSyntaxError(tag, "Element name expected");
}
tag.Name = name;
} else {
tag.Name = string.Empty;
}
bool isXmlDeclr = tag.StartOffset == 0 && tag.Name == "xml";
if (tag.IsStartOrEmptyTag || tag.IsEndTag || isXmlDeclr) {
// Read attributes for the tag
while(true) {
// Chech for all forbiden 'name' charcters first - see ReadName
if (IsEndOfFile()) break;
if (TryPeekWhiteSpace()) {
tag.AddChildren(ReadText(TextType.WhiteSpace));
continue; // End of file might be next
}
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
AXmlAttribute attr = ReadAttribulte();
tag.AddChild(attr);
if (tag.IsEndTag)
OnSyntaxError(tag, attr.StartOffset, attr.EndOffset, "Attribute not allowed in end tag.");
}
} else if (tag.IsDocumentType) {
tag.AddChildren(ReadContentOfDTD());
} else {
int start = this.CurrentLocation;
IEnumerable<AXmlObject> text;
if (tag.IsComment) {
text = ReadText(TextType.Comment);
} else if (tag.IsCData) {
text = ReadText(TextType.CData);
} else if (tag.IsProcessingInstruction) {
text = ReadText(TextType.ProcessingInstruction);
} else if (tag.IsUnknownBang) {
text = ReadText(TextType.UnknownBang);
} else {
throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
}
// Enumerate
text = text.ToList();
// Backtrack at complete start
if (IsEndOfFile() || (tag.IsUnknownBang && TryPeek('<'))) {
GoBack(start);
} else {
tag.AddChildren(text);
}
}
// 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(tag, brStart, brEnd, "'>' or '/>' expected");
} else if (tag.IsEndTag) {
if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
} else if (tag.IsComment) {
if (tag.ClosingBracket != "-->") OnSyntaxError(tag, brStart, brEnd, "'-->' expected");
} else if (tag.IsCData) {
if (tag.ClosingBracket != "]]>") OnSyntaxError(tag, brStart, brEnd, "']]>' expected");
} else if (tag.IsProcessingInstruction) {
if (tag.ClosingBracket != "?>") OnSyntaxError(tag, brStart, brEnd, "'?>' expected");
} else if (tag.IsUnknownBang) {
if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
} else if (tag.IsDocumentType) {
if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
} else {
throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
}
// Attribute name may not apper multiple times
var duplicates = tag.Children.OfType<AXmlAttribute>().GroupBy(attr => attr.Name).SelectMany(g => g.Skip(1));
foreach(AXmlAttribute attr in duplicates) {
OnSyntaxError(tag, attr.StartOffset, attr.EndOffset, "Attribute with name '{0}' already exists", attr.Name);
}
tag.EndOffset = this.CurrentLocation;
OnParsed(tag);
return tag;
}
/// <summary>
/// Reads any of the know opening brackets. (only full bracket)
/// Context: "&lt;"
/// </summary>
string ReadOpeningBracket()
{
// We are using a lot of string literals so that the memory instances are shared
//int start = this.CurrentLocation;
if (TryRead('<')) {
if (TryRead('/')) {
return "</";
} else if (TryRead('?')) {
return "<?";
} else if (TryRead('!')) {
if (TryRead("--")) {
return "<!--";
} else if (TryRead("[CDATA[")) {
return "<![CDATA[";
} else {
foreach(string dtdName in AXmlTag.DtdNames) {
// the dtdName includes "<!"
if (TryRead(dtdName.Remove(0, 2))) return dtdName;
}
return "<!";
}
} else {
return "<";
}
} else {
throw new InternalException("'<' expected");
}
}
/// <summary>
/// Reads any of the know closing brackets. (only full bracket)
/// Context: any
/// </summary>
bool TryReadClosingBracket(out string bracket)
{
// We are using a lot of string literals so that the memory instances are shared
if (TryRead('>')) {
bracket = ">";
} else if (TryRead("/>")) {
bracket = "/>";
} else if (TryRead("?>")) {
bracket = "?>";
} else if (TryRead("-->")) {
bracket = "-->";
} else if (TryRead("]]>")) {
bracket = "]]>";
} else {
bracket = string.Empty;
return false;
}
return true;
}
IEnumerable<AXmlObject> ReadContentOfDTD()
{
int start = this.CurrentLocation;
while(true) {
if (IsEndOfFile()) break; // End of file
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(true) {
if (IsEndOfFile()) break;
TryMoveToAnyOf('<', ']');
if (TryPeek('<')) {
if (start != this.CurrentLocation) { // Two following tags
yield return MakeText(start, this.CurrentLocation);
}
yield return 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) {
yield return MakeText(start, this.CurrentLocation);
}
}
/// <summary>
/// Context: name or "=\'\""
/// </summary>
AXmlAttribute ReadAttribulte()
{
AssertHasMoreData();
AXmlAttribute attr;
if (TryReadFromCacheOrNew(out attr)) return attr;
attr.StartOffset = this.CurrentLocation;
// Read name
string name;
if (TryReadName(out name)) {
if (!IsValidName(name)) {
OnSyntaxError(attr, this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
}
} else {
OnSyntaxError(attr, "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.EqualsSign = GetText(checkpoint, this.CurrentLocation);
} else {
GoBack(checkpoint);
OnSyntaxError(attr, "'=' expected");
attr.EqualsSign = string.Empty;
}
// 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(attr, "White space or end of tag expected");
} else {
OnSyntaxError(attr, "Quote {0} expected (or add whitespace after the following one)", quoteChar);
}
} else {
OnSyntaxError(attr, "White space or end of tag expected");
}
}
} else {
// '<' or end of file
GoBack(valueStart);
ReadAttributeValue(quoteChar);
OnSyntaxError(attr, "Quote {0} expected", quoteChar);
}
} else {
startsWithQuote = false;
int valueStart = this.CurrentLocation;
ReadAttributeValue(null);
TryRead('\"');
TryRead('\'');
if (valueStart == this.CurrentLocation) {
OnSyntaxError(attr, "Attribute value expected");
} else {
OnSyntaxError(attr, valueStart, this.CurrentLocation, "Attribute value must be quoted");
}
}
attr.QuotedValue = GetText(start, this.CurrentLocation);
attr.Value = Unquote(attr.QuotedValue);
attr.Value = Dereference(attr, attr.Value, startsWithQuote ? start + 1 : start);
attr.EndOffset = this.CurrentLocation;
OnParsed(attr);
return attr;
}
/// <summary>
/// Read everything up to quote (excluding), opening/closing tag or attribute signature
/// </summary>
void ReadAttributeValue(char? quote)
{
while(true) {
if (IsEndOfFile()) return;
// 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
string name;
if (TryReadName(out name)) {
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
}
}
AXmlText MakeText(int start, int end)
{
AXmlParser.DebugAssert(end > start, "Empty text");
AXmlText text = new AXmlText() {
StartOffset = start,
EndOffset = end,
EscapedValue = GetText(start, end),
Type = TextType.Other
};
OnParsed(text);
return text;
}
const int maxEntityLength = 16; // The longest build-in one is 10 ("&#1114111;")
const int maxTextFragmentSize = 64;
const int lookAheadLength = (3 * maxTextFragmentSize) / 2; // More so that we do not get small "what was inserted" fragments
/// <summary>
/// Reads text and optionaly separates it into fragments.
/// It can also return empty set for no appropriate text input.
/// Make sure you enumerate it only once
/// </summary>
IEnumerable<AXmlObject> ReadText(TextType type)
{
bool lookahead = false;
while(true) {
AXmlText text;
if (TryReadFromCacheOrNew(out text, t => t.Type == type)) {
// Cached text found
yield return text;
continue; // Read next fragment; the method can handle "no text left"
}
text.Type = type;
// Limit the reading to just a few characters
// (the first character not to be read)
int fragmentEnd = Math.Min(this.CurrentLocation + maxTextFragmentSize, this.InputLength);
// Look if some futher text has been already processed and align so that
// we hit that chache point. It is expensive so it is off for the first run
if (lookahead) {
// Note: Must fit entity
AXmlObject nextFragment = trackedSegments.GetCachedObject<AXmlText>(this.CurrentLocation + maxEntityLength, lookAheadLength - maxEntityLength, t => t.Type == type);
if (nextFragment != null) {
fragmentEnd = Math.Min(nextFragment.StartOffset, this.InputLength);
AXmlParser.Log("Parsing only text ({0}-{1}) because later text was already processed", this.CurrentLocation, fragmentEnd);
}
}
lookahead = true;
text.StartOffset = this.CurrentLocation;
int start = this.CurrentLocation;
// 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(text, this.CurrentLocation, this.CurrentLocation + 3, "']]>' is not allowed in text");
}
TryMoveNext();
continue;
}
throw new Exception("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(text, 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 Exception("Uknown type " + type);
}
text.ContainsOnlyWhitespace = (wsEnd == this.CurrentLocation);
// Terminal found or real end was reached;
bool finished = this.CurrentLocation < fragmentEnd || IsEndOfFile();
if (!finished) {
// We have to continue reading more text fragments
// If there is entity reference, make sure the next segment starts with it to prevent framentation
int entitySearchStart = Math.Max(start + 1 /* data for us */, this.CurrentLocation - maxEntityLength);
int entitySearchLength = this.CurrentLocation - entitySearchStart;
if (entitySearchLength > 0) {
// Note that LastIndexOf works backward
int entityIndex = input.LastIndexOf('&', this.CurrentLocation - 1, entitySearchLength);
if (entityIndex != -1) {
GoBack(entityIndex);
}
}
}
text.EscapedValue = GetText(start, this.CurrentLocation);
if (type == TextType.CharacterData) {
// Normalize end of line first
text.Value = Dereference(text, NormalizeEndOfLine(text.EscapedValue), start);
} else {
text.Value = text.EscapedValue;
}
text.EndOffset = this.CurrentLocation;
if (text.EscapedValue.Length > 0) {
OnParsed(text);
yield return text;
}
if (finished) {
yield break;
}
}
}
#region Helper methods
void OnSyntaxError(AXmlObject obj, string message, params object[] args)
{
OnSyntaxError(obj, this.CurrentLocation, this.CurrentLocation + 1, message, args);
}
public static void OnSyntaxError(AXmlObject obj, int start, int end, string message, params object[] args)
{
if (end <= start) end = start + 1;
string formattedMessage = string.Format(CultureInfo.InvariantCulture, message, args);
AXmlParser.Log("Syntax error ({0}-{1}): {2}", start, end, formattedMessage);
obj.AddSyntaxError(new SyntaxError() {
Object = obj,
StartOffset = start,
EndOffset = end,
Message = formattedMessage,
});
}
static bool IsValidName(string name)
{
try {
System.Xml.XmlConvert.VerifyName(name);
return true;
} catch (System.Xml.XmlException) {
return false;
}
}
/// <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;
}
}
}
static string NormalizeEndOfLine(string text)
{
return text.Replace("\r\n", "\n").Replace("\r", "\n");
}
string Dereference(AXmlObject owner, 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(owner, 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(owner, 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(owner, 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(owner, 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(owner, errorLoc + 2, errorLoc + 1 + name.Length, "Invalid unicode character U+{0:X} ({0})", num);
}
} else {
replacement = null;
}
} else if (!IsValidName(name)) {
replacement = null;
OnSyntaxError(owner, errorLoc + 1, errorLoc + 1, "Invalid entity name");
} else {
replacement = null;
if (parser.UnknownEntityReferenceIsError) {
OnSyntaxError(owner, 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
}
}

39
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TextType.cs

@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary> Identifies the context in which the text occured </summary>
enum TextType
{
/// <summary> Ends with non-whitespace </summary>
WhiteSpace,
/// <summary> Ends with "&lt;"; "]]&gt;" is error </summary>
CharacterData,
/// <summary> Ends with "-->"; "--" is error </summary>
Comment,
/// <summary> Ends with "]]&gt;" </summary>
CData,
/// <summary> Ends with "?>" </summary>
ProcessingInstruction,
/// <summary> Ends with "&lt;" or ">" </summary>
UnknownBang,
/// <summary> Unknown </summary>
Other
}
}

309
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TokenReader.cs

@ -1,309 +0,0 @@ @@ -1,309 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.AvalonEdit.Xml
{
class TokenReader
{
string input;
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.
// Acutal value is max(currentLocation, maxTouchedLocation).
int maxTouchedLocation;
public int InputLength {
get { return inputLength; }
}
public int CurrentLocation {
get { return currentLocation; }
}
public int MaxTouchedLocation {
get { return Math.Max(currentLocation, maxTouchedLocation); }
}
public TokenReader(string input)
{
this.input = input;
this.inputLength = input.Length;
}
protected bool IsEndOfFile()
{
return currentLocation == inputLength;
}
protected bool HasMoreData()
{
return currentLocation < inputLength;
}
protected void AssertHasMoreData()
{
AXmlParser.Assert(HasMoreData(), "Unexpected end of file");
}
protected bool TryMoveNext()
{
if (currentLocation == inputLength) return false;
currentLocation++;
return true;
}
protected void Skip(int count)
{
AXmlParser.Assert(currentLocation + count <= inputLength, "Skipping after the end of file");
currentLocation += count;
}
protected void GoBack(int oldLocation)
{
AXmlParser.Assert(oldLocation <= currentLocation, "Trying to move forward");
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation);
currentLocation = oldLocation;
}
protected bool TryRead(char c)
{
if (currentLocation == inputLength) return false;
if (input[currentLocation] == c) {
currentLocation++;
return true;
} else {
return false;
}
}
protected bool TryReadAnyOf(params char[] c)
{
if (currentLocation == inputLength) return false;
if (c.Contains(input[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[currentLocation - back] == c;
}
protected bool TryPeek(char c)
{
if (currentLocation == inputLength) return false;
return input[currentLocation] == c;
}
protected bool TryPeekAnyOf(params char[] chars)
{
if (currentLocation == inputLength) return false;
return chars.Contains(input[currentLocation]);
}
protected bool TryPeek(string text)
{
if (!TryPeek(text[0])) return false; // Early exit
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation + (text.Length - 1));
// 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.Substring(currentLocation, text.Length) == text;
}
protected bool TryPeekWhiteSpace()
{
if (currentLocation == inputLength) return false;
char c = input[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 - 1;
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[currentLocation];
if (((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
currentLocation++; // Accept white-space
continue;
} else {
return true; // Found non-white-space
}
}
}
/// <summary>
/// Read a name token.
/// The following characters are not allowed:
/// "" End of file
/// " \n\r\t" Whitesapce
/// "=\'\"" Attribute value
/// "&lt;>/?" Tags
/// </summary>
/// <returns> True if read at least one character </returns>
protected bool TryReadName(out string res)
{
int start = currentLocation;
// Keep reading up to invalid character
while(true) {
if (currentLocation == inputLength) break; // Reject end of file
char c = input[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
}
}
if (start == currentLocation) {
res = string.Empty;
return false;
} else {
res = GetText(start, currentLocation);
return true;
}
}
protected string GetText(int start, int end)
{
AXmlParser.Assert(end <= currentLocation, "Reading ahead of current location");
if (start == inputLength && end == inputLength) {
return string.Empty;
} else {
return GetCachedString(input.Substring(start, end - start));
}
}
Dictionary<string, string> stringCache = new Dictionary<string, string>();
int stringCacheRequestedCount;
int stringCacheRequestedSize;
int stringCacheStoredCount;
int stringCacheStoredSize;
string GetCachedString(string cached)
{
stringCacheRequestedCount += 1;
stringCacheRequestedSize += 8 + 2 * cached.Length;
// Do not bother with long strings
if (cached.Length > 32) {
stringCacheStoredCount += 1;
stringCacheStoredSize += 8 + 2 * cached.Length;
return cached;
}
if (stringCache.ContainsKey(cached)) {
// Get the instance from the cache instead
return stringCache[cached];
} else {
// Add to cache
stringCacheStoredCount += 1;
stringCacheStoredSize += 8 + 2 * cached.Length;
stringCache.Add(cached, cached);
return cached;
}
}
public void PrintStringCacheStats()
{
AXmlParser.Log("String cache: Requested {0} ({1} bytes); Actaully stored {2} ({3} bytes); {4}% stored", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheStoredCount, stringCacheStoredSize, stringCacheRequestedSize == 0 ? 0 : stringCacheStoredSize * 100 / stringCacheRequestedSize);
}
}
}

165
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs

@ -1,165 +0,0 @@ @@ -1,165 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Xml
{
/// <summary>
/// Holds all objects that need to keep offsets up to date.
/// </summary>
class TrackedSegmentCollection
{
/// <summary>
/// Holds all types of objects in one collection.
/// </summary>
TextSegmentCollection<TextSegment> segments = new TextSegmentCollection<TextSegment>();
/// <summary>
/// Is used to identify what memory range was touched by object
/// The default is (StartOffset, EndOffset + 1) which is not stored
/// </summary>
class TouchedRange: TextSegment
{
public AXmlObject TouchedByObject { get; set; }
}
public void UpdateOffsetsAndInvalidate(IEnumerable<DocumentChangeEventArgs> changes)
{
foreach(DocumentChangeEventArgs change in changes) {
// Update offsets of all items
segments.UpdateOffsets(change);
// Remove any items affected by the change
AXmlParser.Log("Changed {0}-{1}", change.Offset, change.Offset + change.InsertionLength);
// Removing will cause one of the ends to be set to change.Offset
// FindSegmentsContaining includes any segments touching
// so that conviniently takes care of the +1 byte
var segmentsContainingOffset = segments.FindOverlappingSegments(change.Offset, change.InsertionLength);
foreach(AXmlObject obj in segmentsContainingOffset.OfType<AXmlObject>().Where(o => o.IsCached)) {
InvalidateCache(obj, false);
}
foreach(TouchedRange range in segmentsContainingOffset.OfType<TouchedRange>()) {
AXmlParser.Log("Found that {0} dependeds on ({1}-{2})", range.TouchedByObject, range.StartOffset, range.EndOffset);
InvalidateCache(range.TouchedByObject, true);
segments.Remove(range);
}
}
}
/// <summary>
/// Invlidates all objects. That is, the whole document has changed.
/// </summary>
/// <remarks> We still have to keep the items becuase they might be in the document </remarks>
public void InvalidateAll()
{
AXmlParser.Log("Invalidating all objects");
foreach(AXmlObject obj in segments.OfType<AXmlObject>()) {
obj.IsCached = false;
}
}
/// <summary> Add object to cache, optionally adding extra memory tracking </summary>
public void AddParsedObject(AXmlObject obj, int? maxTouchedLocation)
{
if (!(obj.Length > 0 || obj is AXmlDocument))
AXmlParser.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid object {0}. It has zero length.", obj));
// // Expensive check
// if (obj is AXmlContainer) {
// int objStartOffset = obj.StartOffset;
// int objEndOffset = obj.EndOffset;
// foreach(AXmlObject child in ((AXmlContainer)obj).Children) {
// AXmlParser.Assert(objStartOffset <= child.StartOffset && child.EndOffset <= objEndOffset, "Wrong nesting");
// }
// }
segments.Add(obj);
AddSyntaxErrorsOf(obj);
obj.IsCached = true;
if (maxTouchedLocation != null) {
// location is assumed to be read so the range ends at (location + 1)
// For example eg for "a_" it is (0-2)
TouchedRange range = new TouchedRange() {
StartOffset = obj.StartOffset,
EndOffset = maxTouchedLocation.Value + 1,
TouchedByObject = obj
};
segments.Add(range);
AXmlParser.Log("{0} touched range ({1}-{2})", obj, range.StartOffset, range.EndOffset);
}
}
/// <summary> Removes object with all of its non-cached children </summary>
public void RemoveParsedObject(AXmlObject obj)
{
// Cached objects may be used in the future - do not remove them
if (obj.IsCached) return;
segments.Remove(obj);
RemoveSyntaxErrorsOf(obj);
AXmlParser.Log("Stopped tracking {0}", obj);
AXmlContainer container = obj as AXmlContainer;
if (container != null) {
foreach (AXmlObject child in container.Children) {
RemoveParsedObject(child);
}
}
}
public void AddSyntaxErrorsOf(AXmlObject obj)
{
foreach(SyntaxError syntaxError in obj.MySyntaxErrors) {
segments.Add(syntaxError);
}
}
public void RemoveSyntaxErrorsOf(AXmlObject obj)
{
foreach(SyntaxError syntaxError in obj.MySyntaxErrors) {
segments.Remove(syntaxError);
}
}
IEnumerable<AXmlObject> FindParents(AXmlObject child)
{
int childStartOffset = child.StartOffset;
int childEndOffset = child.EndOffset;
foreach(AXmlObject parent in segments.FindSegmentsContaining(child.StartOffset).OfType<AXmlObject>()) {
// Parent is anyone wholy containg the child
if (parent.StartOffset <= childStartOffset && childEndOffset <= parent.EndOffset && parent != child) {
yield return parent;
}
}
}
/// <summary> Invalidates items, but keeps tracking them </summary>
/// <remarks> Can be called redundantly (from range tacking) </remarks>
void InvalidateCache(AXmlObject obj, bool includeParents)
{
if (includeParents) {
foreach(AXmlObject parent in FindParents(obj)) {
parent.IsCached = false;
AXmlParser.Log("Invalidating cached item {0} (it is parent)", parent);
}
}
obj.IsCached = false;
AXmlParser.Log("Invalidating cached item {0}", obj);
}
public T GetCachedObject<T>(int offset, int lookaheadCount, Predicate<T> conditon) where T: AXmlObject, new()
{
TextSegment obj = segments.FindFirstSegmentWithStartAfter(offset);
while(obj != null && offset <= obj.StartOffset && obj.StartOffset <= offset + lookaheadCount) {
if (obj is T && ((AXmlObject)obj).IsCached && conditon((T)obj)) {
return (T)obj;
}
obj = segments.GetNextSegment(obj);
}
return null;
}
}
}
Loading…
Cancel
Save