Browse Source

XML Parser: Simplified code by introducing RawContainer

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4582 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 17 years ago
parent
commit
cbe7c9f20c
  1. 297
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs
  2. 44
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

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

@ -100,10 +100,59 @@ namespace ICSharpCode.AvalonEdit.XmlParser
System.Diagnostics.Debug.WriteLine("XML Linq: " + format, args); System.Diagnostics.Debug.WriteLine("XML Linq: " + format, args);
} }
internal void OnInserting(RawObject parent, int index) protected XName EncodeXName(string name, string ns)
{
if (string.IsNullOrEmpty(name)) name = "_";
name = XmlConvert.EncodeLocalName(name);
if (ns == null) ns = string.Empty;
ns = XmlConvert.EncodeLocalName(ns);
return XName.Get(name, ns);
}
}
public abstract class RawContainer: RawObject
{
/// <summary>
/// Children of the node. Can be Elements, Attributes, etc...
/// Please do not modify directly!
/// </summary>
public ObservableCollection<RawObject> Children { get; private set; }
public RawContainer()
{
this.Children = new ObservableCollection<RawObject>();
}
public ObservableCollection<RawObject> Helper_Elements {
get {
return new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement);
}
}
public override void UpdateDataFrom(RawObject source)
{ {
LogDom("Inserting {0} at index {1}", this, index); if (this.ReadCallID == source.ReadCallID) return;
this.Parent = parent; base.UpdateDataFrom(source);
RawContainer src = (RawContainer)source;
UpdateChildrenFrom(src.Children);
}
// The following should be the only methods that are ever
// used to modify the children collection
public void AddChild(RawObject item)
{
item.Parent = this;
this.Children.Add(item);
}
protected virtual void Insert(int index, RawObject item)
{
LogDom("Inserting {0} at index {1}", item, index);
item.Parent = this;
this.Children.Insert(index, item);
// if (this.Document != null) { // if (this.Document != null) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { // foreach(RawObject obj in GetSeftAndAllNestedObjects()) {
// this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj }); // this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj });
@ -111,10 +160,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// } // }
} }
internal void OnRemoving(RawObject parent, int index) protected virtual void RemoveAt(int index)
{ {
LogDom("Removing {0} at index {1}", this, index); LogDom("Removing {0} at index {1}", this.Children[index], index);
this.Parent = null; this.Children[index].Parent = null;
this.Children.RemoveAt(index);
// if (this.Document != null) { // if (this.Document != null) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) { // foreach(RawObject obj in GetSeftAndAllNestedObjects()) {
// this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj }); // this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj });
@ -122,31 +172,92 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// } // }
} }
protected XName EncodeXName(string name, string ns) /// <summary>
/// Copy items from source list over to destination list.
/// Prefer updating items with matching offsets.
/// </summary>
public void UpdateChildrenFrom(IList<RawObject> srcList)
{ {
if (string.IsNullOrEmpty(name)) name = "_"; IList<RawObject> dstList = this.Children;
name = XmlConvert.EncodeLocalName(name);
if (ns == null) ns = string.Empty;
ns = XmlConvert.EncodeLocalName(ns);
return XName.Get(name, ns); // Items up to 'i' shall be matching
int i = 0;
// Do not do anything smart with the start tag
if (this is RawElement) {
dstList[0].UpdateDataFrom(srcList[0]);
i++;
}
while(i < srcList.Count) {
// Item is missing - 'i' is invalid index
if (i >= dstList.Count) {
Insert(i, srcList[i]);
i++; continue;
}
RawObject srcItem = srcList[i];
RawObject dstItem = dstList[i];
// Matching and updated
if (srcItem.ReadCallID == dstItem.ReadCallID) {
i++; continue;
}
// Offsets and types are matching
if (srcItem.StartOffset == dstItem.StartOffset &&
srcItem.GetType() == dstItem.GetType())
{
dstItem.UpdateDataFrom(srcItem);
i++; continue;
}
// Try to be smart by inserting or removing items
// Dst offset matches with future src
for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) {
RawObject src = srcList[srcItemIndex];
if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) {
for(int j = i; j < srcItemIndex; j++) {
Insert(j, srcList[j]);
}
i = srcItemIndex;
goto continue2;
}
}
// Scr offset matches with future dst
for(int dstItemIndex = i; dstItemIndex < dstList.Count; dstItemIndex++) {
RawObject dst = dstList[dstItemIndex];
if (srcItem.StartOffset == dst.StartOffset && srcItem.GetType() == dst.GetType()) {
for(int j = 0; j < dstItemIndex - i; j++) {
RemoveAt(i);
}
goto continue2;
}
}
// No matches found - just update
if (dstItem.GetType() == srcItem.GetType()) {
dstItem.UpdateDataFrom(srcItem);
i++; continue;
}
// Remove whitespace in hope that element update will occur next
if (dstItem is RawText) {
RemoveAt(i);
continue;
}
// Otherwise just add the item
{
Insert(i, srcList[i]);
i++; continue;
}
// Continue for inner loops
continue2:;
}
// Remove extra items
while(dstList.Count > srcList.Count) {
RemoveAt(srcList.Count);
}
} }
} }
public class RawDocument: RawObject public class RawDocument: RawContainer
{ {
public ObservableCollection<RawObject> Children { get; set; }
// public event EventHandler<RawObjectEventArgs> ObjectAttached; // public event EventHandler<RawObjectEventArgs> ObjectAttached;
// public event EventHandler<RawObjectEventArgs> ObjectDettached; // public event EventHandler<RawObjectEventArgs> ObjectDettached;
public ObservableCollection<RawObject> Helper_Elements {
get {
return new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement);
}
}
// internal void OnObjectAttached(RawObjectEventArgs e) // internal void OnObjectAttached(RawObjectEventArgs e)
// { // {
// if (ObjectAttached != null) ObjectAttached(this, e); // if (ObjectAttached != null) ObjectAttached(this, e);
@ -157,19 +268,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// if (ObjectDettached != null) ObjectDettached(this, e); // if (ObjectDettached != null) ObjectDettached(this, e);
// } // }
public RawDocument()
{
this.Children = new ObservableCollection<RawObject>();
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;
base.UpdateDataFrom(source);
RawDocument src = (RawDocument)source;
RawUtils.SmartListUpdate(src.Children, this.Children, this);
}
public XDocument CreateXDocument(bool autoUpdate) public XDocument CreateXDocument(bool autoUpdate)
{ {
LogLinq("Creating XDocument"); LogLinq("Creating XDocument");
@ -198,19 +296,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
} }
public class RawTag: RawObject public class RawTag: RawContainer
{ {
public string OpeningBracket { get; set; } // "<" or "</" public string OpeningBracket { get; set; } // "<" or "</"
public string Namesapce { get; set; } public string Namesapce { get; set; }
public string Name { get; set; } public string Name { get; set; }
public ObservableCollection<RawObject> Attributes { get; set; }
public string ClosingBracket { get; set; } // ">" or "/>" for well formed public string ClosingBracket { get; set; } // ">" or "/>" for well formed
public RawTag()
{
this.Attributes = new ObservableCollection<RawObject>();
}
public override void UpdateDataFrom(RawObject source) public override void UpdateDataFrom(RawObject source)
{ {
if (this.ReadCallID == source.ReadCallID) return; if (this.ReadCallID == source.ReadCallID) return;
@ -227,59 +319,42 @@ namespace ICSharpCode.AvalonEdit.XmlParser
this.ClosingBracket = src.ClosingBracket; this.ClosingBracket = src.ClosingBracket;
OnLocalDataChanged(); OnLocalDataChanged();
} }
RawUtils.SmartListUpdate(src.Attributes, this.Attributes, this);
} }
public override string ToString() public override string ToString()
{ {
return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Attributes.Count); return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count);
} }
} }
public class RawElement: RawObject public class RawElement: RawContainer
{ {
public RawTag StartTag { get; set; } public RawTag StartTag {
public ObservableCollection<RawObject> Children { get; set; } get {
public bool HasEndTag { get; set; } return (RawTag)this.Children[0];
public RawTag EndTag { get; set; } }
}
public ObservableCollection<RawObject> Helper_AttributesAndElements { public ObservableCollection<RawObject> Helper_AttributesAndElements {
get { get {
return new MergedObservableCollection<RawObject>( return new MergedObservableCollection<RawObject>(
new FilteredObservableCollection<RawObject>(this.StartTag.Attributes, x => x is RawAttribute), new FilteredObservableCollection<RawObject>(this.StartTag.Children, x => x is RawAttribute),
new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement) new FilteredObservableCollection<RawObject>(this.Children, x => x is RawElement)
); );
} }
} }
public RawElement()
{
this.StartTag = new RawTag() { Parent = this };
this.Children = new ObservableCollection<RawObject>();
this.EndTag = new RawTag() { Parent = this };
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;
base.UpdateDataFrom(source);
RawElement src = (RawElement)source;
this.StartTag.UpdateDataFrom(src.StartTag);
RawUtils.SmartListUpdate(src.Children, this.Children, this);
this.EndTag.UpdateDataFrom(src.EndTag);
}
public XElement CreateXElement(bool autoUpdate) public XElement CreateXElement(bool autoUpdate)
{ {
LogLinq("Creating XElement '{0}'", this.StartTag.Name); LogLinq("Creating XElement '{0}'", this.StartTag.Name);
XElement elem = new XElement(EncodeXName(this.StartTag.Name, this.EndTag.Namesapce)); XElement elem = new XElement(EncodeXName(this.StartTag.Name, this.StartTag.Namesapce));
elem.AddAnnotation(this); elem.AddAnnotation(this);
UpdateXElement(elem, autoUpdate); UpdateXElement(elem, autoUpdate);
UpdateXElementAttributes(elem, autoUpdate); UpdateXElementAttributes(elem, autoUpdate);
UpdateXElementChildren(elem, autoUpdate); UpdateXElementChildren(elem, autoUpdate);
if (autoUpdate) { if (autoUpdate) {
this.StartTag.LocalDataChanged += delegate { UpdateXElement(elem, autoUpdate); }; this.StartTag.LocalDataChanged += delegate { UpdateXElement(elem, autoUpdate); };
this.StartTag.Attributes.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); }; this.StartTag.Children.CollectionChanged += delegate { UpdateXElementAttributes(elem, autoUpdate); };
this.Children.CollectionChanged += delegate { UpdateXElementChildren(elem, autoUpdate); }; this.Children.CollectionChanged += delegate { UpdateXElementChildren(elem, autoUpdate); };
} }
return elem; return elem;
@ -288,13 +363,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
void UpdateXElement(XElement elem, bool autoUpdate) void UpdateXElement(XElement elem, bool autoUpdate)
{ {
LogLinq("Updating XElement '{0}'", this.StartTag.Name); LogLinq("Updating XElement '{0}'", this.StartTag.Name);
elem.Name = EncodeXName(this.StartTag.Name, this.EndTag.Namesapce); elem.Name = EncodeXName(this.StartTag.Name, this.StartTag.Namesapce);
} }
internal void UpdateXElementAttributes(XElement elem, bool autoUpdate) internal void UpdateXElementAttributes(XElement elem, bool autoUpdate)
{ {
List<XAttribute> xAttrs = new List<XAttribute>(); List<XAttribute> xAttrs = new List<XAttribute>();
foreach(RawAttribute attr in this.StartTag.Attributes.OfType<RawAttribute>()) { foreach(RawAttribute attr in this.StartTag.Children.OfType<RawAttribute>()) {
XAttribute existing = elem.Attributes().FirstOrDefault(x => x.GetRawObject() == attr); XAttribute existing = elem.Attributes().FirstOrDefault(x => x.GetRawObject() == attr);
xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate)); xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate));
} }
@ -313,7 +388,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public override string ToString() public override string ToString()
{ {
return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Attributes.Count, this.Children.Count); return string.Format("[{0} '{1}{2}{3}' Attr:{4} Chld:{5}]", base.ToString(), this.StartTag.OpeningBracket, this.StartTag.Name, this.StartTag.ClosingBracket, this.StartTag.Children.Count, this.Children.Count);
} }
} }
@ -417,83 +492,5 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (xObj == null) return null; if (xObj == null) return null;
return xObj.Annotation<RawObject>(); return xObj.Annotation<RawObject>();
} }
/// <summary>
/// Copy items from source list over to destination list.
/// Prefer updating items with matching offsets.
/// </summary>
public static void SmartListUpdate(IList<RawObject> srcList, IList<RawObject> dstList, RawObject dstListOwner)
{
// Items up to 'i' shall be matching
for(int i = 0; i < srcList.Count;) {
// Item is missing - 'i' is invalid index
if (i >= dstList.Count) {
srcList[i].OnInserting(dstListOwner, i);
dstList.Insert(i, srcList[i]);
i++; continue;
}
RawObject srcItem = srcList[i];
RawObject dstItem = dstList[i];
// Matching and updated
if (srcItem.ReadCallID == dstItem.ReadCallID) {
i++; continue;
}
// Offsets and types are matching
if (srcItem.StartOffset == dstItem.StartOffset &&
srcItem.GetType() == dstItem.GetType())
{
dstItem.UpdateDataFrom(srcItem);
i++; continue;
}
// Try to be smart by inserting or removing items
// Dst offset matches with future src
for(int srcItemIndex = i; srcItemIndex < srcList.Count; srcItemIndex++) {
RawObject src = srcList[srcItemIndex];
if (src.StartOffset == dstItem.StartOffset && src.GetType() == dstItem.GetType()) {
for(int j = i; j < srcItemIndex; j++) {
srcList[j].OnInserting(dstListOwner, j);
dstList.Insert(j, srcList[j]);
}
i = srcItemIndex;
goto continue2;
}
}
// Scr offset matches with future dst
for(int dstItemIndex = i; dstItemIndex < dstList.Count; dstItemIndex++) {
RawObject dst = dstList[dstItemIndex];
if (srcItem.StartOffset == dst.StartOffset && srcItem.GetType() == dst.GetType()) {
for(int j = 0; j < dstItemIndex - i; j++) {
dstList[i].OnRemoving(dstListOwner, i);
dstList.RemoveAt(i);
}
goto continue2;
}
}
// No matches found - just update
if (dstItem.GetType() == srcItem.GetType()) {
dstItem.UpdateDataFrom(srcItem);
i++; continue;
}
// Remove whitespace in hope that element update will occur next
if (dstItem is RawText) {
dstList[i].OnRemoving(dstListOwner, i);
dstList.RemoveAt(i);
continue;
}
// Otherwise just add the item
{
srcList[i].OnInserting(dstListOwner, i);
dstList.Insert(i, srcList[i]);
i++; continue;
}
}
// Remove extra items
while(dstList.Count > srcList.Count) {
dstList[srcList.Count].OnRemoving(dstListOwner, srcList.Count);
dstList.RemoveAt(srcList.Count);
}
// Continue for inner loops
continue2:;
}
} }
} }

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

@ -192,9 +192,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (IsEndOfFile()) { if (IsEndOfFile()) {
break; break;
} else if (TryPeek('<')) { } else if (TryPeek('<')) {
doc.Children.Add(ReadElement(doc)); doc.AddChild(ReadElement());
} else { } else {
doc.Children.Add(ReadCharacterData(doc)); doc.AddChild(ReadCharacterData());
} }
} }
doc.EndOffset = currentLocation; doc.EndOffset = currentLocation;
@ -204,17 +204,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return doc; return doc;
} }
RawElement ReadElement(RawObject parent) RawElement ReadElement()
{ {
Debug.Assert(HasMoreData() && TryPeek('<')); Debug.Assert(HasMoreData() && TryPeek('<'));
RawElement element = ReadFromCache<RawElement>(currentLocation); RawElement element = ReadFromCache<RawElement>(currentLocation);
if (element != null) return element; if (element != null) return element;
element = new RawElement() { Parent = parent }; element = new RawElement();
element.StartOffset = currentLocation; element.StartOffset = currentLocation;
element.StartTag = ReadTag(element); // Read start tag
element.AddChild(ReadTag());
// Read content // Read content
if (element.StartTag.ClosingBracket == ">" && if (element.StartTag.ClosingBracket == ">" &&
element.StartTag.OpeningBracket != "<?" && element.StartTag.OpeningBracket != "<?" &&
@ -226,16 +227,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser
break; break;
} else if (TryPeek('<')) { } else if (TryPeek('<')) {
if (TryPeek("</")) break; if (TryPeek("</")) break;
element.Children.Add(ReadElement(element)); element.AddChild(ReadElement());
} else { } else {
element.Children.Add(ReadCharacterData(element)); element.AddChild(ReadCharacterData());
} }
} }
} }
// Read closing tag // Read end tag
if (TryPeek("</")) { if (TryPeek("</")) {
element.HasEndTag = true; element.AddChild(ReadTag());
element.EndTag = ReadTag(element);
} }
element.EndOffset = currentLocation; element.EndOffset = currentLocation;
@ -244,14 +244,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return element; return element;
} }
RawTag ReadTag(RawObject parent) RawTag ReadTag()
{ {
Debug.Assert(HasMoreData() && TryPeek('<')); Debug.Assert(HasMoreData() && TryPeek('<'));
RawTag tag = ReadFromCache<RawTag>(currentLocation); RawTag tag = ReadFromCache<RawTag>(currentLocation);
if (tag != null) return tag; if (tag != null) return tag;
tag = new RawTag() { Parent = parent }; tag = new RawTag();
tag.StartOffset = currentLocation; tag.StartOffset = currentLocation;
if (TryRead('<')) { if (TryRead('<')) {
@ -276,7 +276,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// Read attributes // Read attributes
while(true) { while(true) {
if (IsWhiteSpace() == true) { if (IsWhiteSpace() == true) {
tag.Attributes.Add(ReadWhiteSpace(tag)); tag.AddChild(ReadWhiteSpace());
} }
if (TryRead('>')) { if (TryRead('>')) {
tag.ClosingBracket = ">"; tag.ClosingBracket = ">";
@ -296,7 +296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
if (TryPeek('<')) break; if (TryPeek('<')) break;
if (HasMoreData()) { if (HasMoreData()) {
tag.Attributes.Add(ReadAttribulte(tag)); tag.AddChild(ReadAttribulte());
continue; continue;
} }
break; break;
@ -308,14 +308,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return tag; return tag;
} }
RawText ReadWhiteSpace(RawObject parent) RawText ReadWhiteSpace()
{ {
Debug.Assert(HasMoreData() && IsWhiteSpace() == true); Debug.Assert(HasMoreData() && IsWhiteSpace() == true);
RawText ws = ReadFromCache<RawText>(currentLocation); RawText ws = ReadFromCache<RawText>(currentLocation);
if (ws != null) return ws; if (ws != null) return ws;
ws = new RawText() { Parent = parent }; ws = new RawText();
ws.StartOffset = currentLocation; ws.StartOffset = currentLocation;
int start = currentLocation; int start = currentLocation;
@ -327,14 +327,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return ws; return ws;
} }
RawAttribute ReadAttribulte(RawObject parent) RawAttribute ReadAttribulte()
{ {
Debug.Assert(HasMoreData()); Debug.Assert(HasMoreData());
RawAttribute attr = ReadFromCache<RawAttribute>(currentLocation); RawAttribute attr = ReadFromCache<RawAttribute>(currentLocation);
if (attr != null) return attr; if (attr != null) return attr;
attr = new RawAttribute() { Parent = parent }; attr = new RawAttribute();
attr.StartOffset = currentLocation; attr.StartOffset = currentLocation;
if (HasMoreData()) { if (HasMoreData()) {
@ -346,10 +346,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
int checkpoint = currentLocation; int checkpoint = currentLocation;
attr.EqualsSign = string.Empty; attr.EqualsSign = string.Empty;
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value;
if (TryRead('=')) { if (TryRead('=')) {
attr.EqualsSign += "="; attr.EqualsSign += "=";
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value; if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value;
if (IsWhiteSpaceOrReserved() == false) { if (IsWhiteSpaceOrReserved() == false) {
// Read attribute value // Read attribute value
int start = currentLocation; int start = currentLocation;
@ -375,14 +375,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return attr; return attr;
} }
RawText ReadCharacterData(RawObject parent) RawText ReadCharacterData()
{ {
Debug.Assert(HasMoreData()); Debug.Assert(HasMoreData());
RawText charData = ReadFromCache<RawText>(currentLocation); RawText charData = ReadFromCache<RawText>(currentLocation);
if (charData != null) return charData; if (charData != null) return charData;
charData = new RawText() { Parent = parent }; charData = new RawText();
charData.StartOffset = currentLocation; charData.StartOffset = currentLocation;
int start = currentLocation; int start = currentLocation;

Loading…
Cancel
Save