Browse Source

XML Parser: Cache the XLinq objects

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4585 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 16 years ago
parent
commit
c6a524da5d
  1. 200
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs
  2. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

200
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs

@ -39,17 +39,17 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public RawObject Parent { get; set; } public RawObject Parent { get; set; }
// public RawDocument Document { public RawDocument Document {
// get { get {
// if (this.Parent != null) { if (this.Parent != null) {
// return this.Parent.Document; return this.Parent.Document;
// } else if (this is RawDocument) { } else if (this is RawDocument) {
// return (RawDocument)this; return (RawDocument)this;
// } else { } else {
// return null; return null;
// } }
// } }
// } }
/// <summary> Occurs when the value of any local properties changes. Nested changes do not cause the event to occur </summary> /// <summary> Occurs when the value of any local properties changes. Nested changes do not cause the event to occur </summary>
public event EventHandler LocalDataChanged; public event EventHandler LocalDataChanged;
@ -76,6 +76,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
this.ReadCallID = new object(); this.ReadCallID = new object();
} }
public virtual IEnumerable<RawObject> GetSelfAndAllChildren()
{
return new RawObject[] { this };
}
public virtual void UpdateDataFrom(RawObject source) public virtual void UpdateDataFrom(RawObject source)
{ {
this.ReadCallID = source.ReadCallID; this.ReadCallID = source.ReadCallID;
@ -139,6 +144,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
UpdateChildrenFrom(src.Children); UpdateChildrenFrom(src.Children);
} }
public override IEnumerable<RawObject> GetSelfAndAllChildren()
{
return Enumerable.Union(
new RawContainer[] { this },
this.Children.SelectMany(x => x.GetSelfAndAllChildren())
);
}
// The following should be the only methods that are ever // The following should be the only methods that are ever
// used to modify the children collection // used to modify the children collection
@ -153,23 +166,24 @@ namespace ICSharpCode.AvalonEdit.XmlParser
LogDom("Inserting {0} at index {1}", item, index); LogDom("Inserting {0} at index {1}", item, index);
item.Parent = this; item.Parent = this;
this.Children.Insert(index, item); this.Children.Insert(index, item);
// if (this.Document != null) { if (this.Document != null) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { foreach(RawObject obj in GetSelfAndAllChildren()) {
// this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj }); this.Document.OnObjectAttached(item);
// } }
// } }
} }
protected virtual void RemoveAt(int index) protected virtual void RemoveAt(int index)
{ {
LogDom("Removing {0} at index {1}", this.Children[index], index); RawObject removedItem = this.Children[index];
this.Children[index].Parent = null; LogDom("Removing {0} at index {1}", removedItem, index);
removedItem.Parent = null;
this.Children.RemoveAt(index); this.Children.RemoveAt(index);
// if (this.Document != null) { if (this.Document != null) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { foreach(RawObject obj in GetSelfAndAllChildren()) {
// this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj }); this.Document.OnObjectDettached(removedItem);
// } }
// } }
} }
/// <summary> /// <summary>
@ -255,37 +269,43 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public class RawDocument: RawContainer public class RawDocument: RawContainer
{ {
// public event EventHandler<RawObjectEventArgs> ObjectAttached; public event EventHandler<RawObjectEventArgs> ObjectAttached;
// public event EventHandler<RawObjectEventArgs> ObjectDettached; public event EventHandler<RawObjectEventArgs> ObjectDettached;
// internal void OnObjectAttached(RawObjectEventArgs e) internal void OnObjectAttached(RawObject obj)
// { {
// if (ObjectAttached != null) ObjectAttached(this, e); if (ObjectAttached != null) ObjectAttached(this, new RawObjectEventArgs() { Object = obj } );
// } }
//
// internal void OnObjectDettached(RawObjectEventArgs e)
// {
// if (ObjectDettached != null) ObjectDettached(this, e);
// }
public XDocument CreateXDocument(bool autoUpdate) internal void OnObjectDettached(RawObject obj)
{ {
if (ObjectDettached != null) ObjectDettached(this, new RawObjectEventArgs() { Object = obj } );
}
XDocument xDoc;
public XDocument GetXDocument()
{
if (xDoc == null) {
LogLinq("Creating XDocument"); LogLinq("Creating XDocument");
XDocument doc = new XDocument(); xDoc = new XDocument();
doc.AddAnnotation(this); xDoc.AddAnnotation(this);
UpdateXDocument(doc, autoUpdate); UpdateXDocumentChildren(true);
this.Children.CollectionChanged += delegate { UpdateXDocument(doc, autoUpdate); }; this.Children.CollectionChanged += delegate { UpdateXDocumentChildren(false); };
return doc; }
return xDoc;
} }
void UpdateXDocument(XDocument doc, bool autoUpdate) void UpdateXDocumentChildren(bool firstUpdate)
{ {
if (!firstUpdate) LogLinq("Updating XDocument Children");
RawElement root = this.Children.OfType<RawElement>().FirstOrDefault(x => x.StartTag.OpeningBracket == "<"); RawElement root = this.Children.OfType<RawElement>().FirstOrDefault(x => x.StartTag.OpeningBracket == "<");
if (doc.Root.GetRawObject() != root) { if (xDoc.Root.GetRawObject() != root) {
if (root != null) { if (root != null) {
doc.ReplaceNodes(root.CreateXElement(autoUpdate)); xDoc.ReplaceNodes(root.GetXElement());
} else { } else {
doc.RemoveNodes(); xDoc.RemoveNodes();
} }
} }
} }
@ -344,46 +364,48 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
} }
public XElement CreateXElement(bool autoUpdate) XElement xElem;
public XElement GetXElement()
{ {
if (xElem == null) {
LogLinq("Creating XElement '{0}'", this.StartTag.Name); LogLinq("Creating XElement '{0}'", this.StartTag.Name);
XElement elem = new XElement(EncodeXName(this.StartTag.Name, this.StartTag.Namesapce)); xElem = new XElement(EncodeXName(this.StartTag.Name, this.StartTag.Namesapce));
elem.AddAnnotation(this); xElem.AddAnnotation(this);
UpdateXElement(elem, autoUpdate); UpdateXElement(true);
UpdateXElementAttributes(elem, autoUpdate); UpdateXElementAttributes(true);
UpdateXElementChildren(elem, autoUpdate); UpdateXElementChildren(true);
if (autoUpdate) { this.StartTag.LocalDataChanged += delegate { UpdateXElement(false); };
this.StartTag.LocalDataChanged += delegate { UpdateXElement(elem, autoUpdate); }; this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(false); };
this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); }; this.Children.CollectionChanged += delegate { UpdateXElementChildren(false); };
this.Children.CollectionChanged += delegate { UpdateXElementChildren(elem, autoUpdate); };
} }
return elem; return xElem;
} }
void UpdateXElement(XElement elem, bool autoUpdate) void UpdateXElement(bool firstUpdate)
{ {
LogLinq("Updating XElement '{0}'", this.StartTag.Name); if (!firstUpdate) LogLinq("Updating XElement '{0}'", this.StartTag.Name);
elem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce);
xElem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce);
} }
internal void UpdateXElementAttributes(XElement elem, bool autoUpdate) internal void UpdateXElementAttributes(bool firstUpdate)
{ {
List<XAttribute> xAttrs = new List<XAttribute>(); if (!firstUpdate) LogLinq("Updating XElement Attributes of '{0}'", this.StartTag.Name);
foreach(RawAttribute attr in this.StartTag.Children.OfType<RawAttribute>()) {
XAttribute existing = elem.Attributes().FirstOrDefault(x => x.GetRawObject() == attr); xElem.ReplaceAttributes(); // Otherwise we get duplicate item exception
xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate)); xElem.ReplaceAttributes(
} this.StartTag.Children.OfType<RawAttribute>().Select(x => x.GetXAttribute()).ToArray()
elem.ReplaceAttributes(xAttrs.ToArray()); );
} }
void UpdateXElementChildren(XElement elem, bool autoUpdate) void UpdateXElementChildren(bool firstUpdate)
{ {
List<XElement> xElems = new List<XElement>(); if (!firstUpdate) LogLinq("Updating XElement Children of '{0}'", this.StartTag.Name);
foreach(RawElement rawElem in this.Children.OfType<RawElement>()) {
XElement existing = (XElement)elem.Nodes().FirstOrDefault(x => x.GetRawObject() == rawElem); xElem.ReplaceNodes(
xElems.Add(existing ?? rawElem.CreateXElement(autoUpdate)); this.Children.OfType<RawElement>().Select(x => x.GetXElement()).ToArray()
} );
elem.ReplaceNodes(xElems);
} }
public override string ToString() public override string ToString()
@ -417,28 +439,32 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
} }
public XAttribute CreateXAttribute(bool autoUpdate) XAttribute xAttr;
public XAttribute GetXAttribute()
{ {
if (xAttr == null) {
LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.Value); LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.Value);
XAttribute attr = new XAttribute(EncodeXName(this.Name, this.Namesapce), string.Empty); xAttr = new XAttribute(EncodeXName(this.Name, this.Namesapce), string.Empty);
attr.AddAnnotation(this); xAttr.AddAnnotation(this);
bool deleted = false; bool deleted = false;
UpdateXAttribute(attr, autoUpdate, ref deleted); UpdateXAttribute(true, ref deleted);
if (autoUpdate) this.LocalDataChanged += delegate { UpdateXAttribute(attr, autoUpdate, ref deleted); }; this.LocalDataChanged += delegate { if (!deleted) UpdateXAttribute(false, ref deleted); };
return attr; }
return xAttr;
} }
void UpdateXAttribute(XAttribute attr, bool autoUpdate, ref bool deleted) void UpdateXAttribute(bool firstUpdate, ref bool deleted)
{ {
if (deleted) return; if (!firstUpdate) LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.Value);
LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.Value);
if (attr.Name == EncodeXName(this.Name, this.Namesapce)) { if (xAttr.Name == EncodeXName(this.Name, this.Namesapce)) {
attr.Value = this.Value ?? string.Empty; xAttr.Value = this.Value ?? string.Empty;
} else { } else {
XElement parent = attr.Parent; xAttr.Remove();
attr.Remove(); xAttr = null;
deleted = true; deleted = true; // No longer get events for this instance
((RawElement)parent.GetRawObject()).UpdateXElementAttributes(parent, autoUpdate); ((RawElement)this.Parent.Parent).UpdateXElementAttributes(false);
} }
} }

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public XmlParser(TextDocument textDocument) public XmlParser(TextDocument textDocument)
{ {
this.userLinqDocument = userDocument.CreateXDocument(true); this.userLinqDocument = userDocument.GetXDocument();
this.textDocument = textDocument; this.textDocument = textDocument;
this.textDocument.Changed += delegate(object sender, DocumentChangeEventArgs e) { this.textDocument.Changed += delegate(object sender, DocumentChangeEventArgs e) {
changesSinceLastParse.Add(e); changesSinceLastParse.Add(e);
@ -50,12 +50,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser
end = Math.Max(Math.Min(end, textDocument.TextLength - 1), 0); end = Math.Max(Math.Min(end, textDocument.TextLength - 1), 0);
foreach(RawObject obj in parsedItems.FindOverlappingSegments(start, end - start)) { foreach(RawObject obj in parsedItems.FindOverlappingSegments(start, end - start)) {
parsedItems.Remove(obj); parsedItems.Remove(obj);
Log("Removed cached item: {0}", obj); Log("Removed cached item {0}", obj);
} }
} }
changesSinceLastParse.Clear(); changesSinceLastParse.Clear();
RawDocument parsedDocument = ReadDocument(); RawDocument parsedDocument = ReadDocument();
if (parsedDocument.ReadCallID != userDocument.ReadCallID) {
RawObject.LogDom("Updating main DOM tree...");
}
userDocument.UpdateDataFrom(parsedDocument); userDocument.UpdateDataFrom(parsedDocument);
return userDocument; return userDocument;
} }

Loading…
Cancel
Save