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 @@ -100,10 +100,59 @@ namespace ICSharpCode.AvalonEdit.XmlParser
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()
{
LogDom("Inserting {0} at index {1}", this, index);
this.Parent = parent;
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)
{
if (this.ReadCallID == source.ReadCallID) return;
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) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) {
// this.Document.OnObjectAttached(new RawObjectEventArgs() { Object = obj });
@ -111,10 +160,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -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);
this.Parent = null;
LogDom("Removing {0} at index {1}", this.Children[index], index);
this.Children[index].Parent = null;
this.Children.RemoveAt(index);
// if (this.Document != null) {
// foreach(RawObject obj in GetSeftAndAllNestedObjects()) {
// this.Document.OnObjectDettached(new RawObjectEventArgs() { Object = obj });
@ -122,31 +172,92 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -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 = "_";
name = XmlConvert.EncodeLocalName(name);
if (ns == null) ns = string.Empty;
ns = XmlConvert.EncodeLocalName(ns);
IList<RawObject> dstList = this.Children;
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;
}
public class RawDocument: RawObject
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
{
public ObservableCollection<RawObject> Children { get; set; }
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: RawContainer
{
// public event EventHandler<RawObjectEventArgs> ObjectAttached;
// 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)
// {
// if (ObjectAttached != null) ObjectAttached(this, e);
@ -157,19 +268,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -157,19 +268,6 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// 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)
{
LogLinq("Creating XDocument");
@ -198,19 +296,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -198,19 +296,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
}
public class RawTag: RawObject
public class RawTag: RawContainer
{
public string OpeningBracket { get; set; } // "<" or "</"
public string Namesapce { get; set; }
public string Name { get; set; }
public ObservableCollection<RawObject> Attributes { get; set; }
public string ClosingBracket { get; set; } // ">" or "/>" for well formed
public RawTag()
{
this.Attributes = new ObservableCollection<RawObject>();
}
public override void UpdateDataFrom(RawObject source)
{
if (this.ReadCallID == source.ReadCallID) return;
@ -227,59 +319,42 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -227,59 +319,42 @@ namespace ICSharpCode.AvalonEdit.XmlParser
this.ClosingBracket = src.ClosingBracket;
OnLocalDataChanged();
}
RawUtils.SmartListUpdate(src.Attributes, this.Attributes, this);
}
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 ObservableCollection<RawObject> Children { get; set; }
public bool HasEndTag { get; set; }
public RawTag EndTag { get; set; }
public RawTag StartTag {
get {
return (RawTag)this.Children[0];
}
}
public ObservableCollection<RawObject> Helper_AttributesAndElements {
get {
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)
);
}
}
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)
{
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);
UpdateXElement(elem, autoUpdate);
UpdateXElementAttributes(elem, autoUpdate);
UpdateXElementChildren(elem, autoUpdate);
if (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); };
}
return elem;
@ -288,13 +363,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -288,13 +363,13 @@ namespace ICSharpCode.AvalonEdit.XmlParser
void UpdateXElement(XElement elem, bool autoUpdate)
{
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)
{
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);
xAttrs.Add(existing ?? attr.CreateXAttribute(autoUpdate));
}
@ -313,7 +388,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -313,7 +388,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
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 @@ -417,83 +492,5 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (xObj == null) return null;
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 @@ -192,9 +192,9 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (IsEndOfFile()) {
break;
} else if (TryPeek('<')) {
doc.Children.Add(ReadElement(doc));
doc.AddChild(ReadElement());
} else {
doc.Children.Add(ReadCharacterData(doc));
doc.AddChild(ReadCharacterData());
}
}
doc.EndOffset = currentLocation;
@ -204,17 +204,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -204,17 +204,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return doc;
}
RawElement ReadElement(RawObject parent)
RawElement ReadElement()
{
Debug.Assert(HasMoreData() && TryPeek('<'));
RawElement element = ReadFromCache<RawElement>(currentLocation);
if (element != null) return element;
element = new RawElement() { Parent = parent };
element = new RawElement();
element.StartOffset = currentLocation;
element.StartTag = ReadTag(element);
// Read start tag
element.AddChild(ReadTag());
// Read content
if (element.StartTag.ClosingBracket == ">" &&
element.StartTag.OpeningBracket != "<?" &&
@ -226,16 +227,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -226,16 +227,15 @@ namespace ICSharpCode.AvalonEdit.XmlParser
break;
} else if (TryPeek('<')) {
if (TryPeek("</")) break;
element.Children.Add(ReadElement(element));
element.AddChild(ReadElement());
} else {
element.Children.Add(ReadCharacterData(element));
element.AddChild(ReadCharacterData());
}
}
}
// Read closing tag
// Read end tag
if (TryPeek("</")) {
element.HasEndTag = true;
element.EndTag = ReadTag(element);
element.AddChild(ReadTag());
}
element.EndOffset = currentLocation;
@ -244,14 +244,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -244,14 +244,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return element;
}
RawTag ReadTag(RawObject parent)
RawTag ReadTag()
{
Debug.Assert(HasMoreData() && TryPeek('<'));
RawTag tag = ReadFromCache<RawTag>(currentLocation);
if (tag != null) return tag;
tag = new RawTag() { Parent = parent };
tag = new RawTag();
tag.StartOffset = currentLocation;
if (TryRead('<')) {
@ -276,7 +276,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -276,7 +276,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
// Read attributes
while(true) {
if (IsWhiteSpace() == true) {
tag.Attributes.Add(ReadWhiteSpace(tag));
tag.AddChild(ReadWhiteSpace());
}
if (TryRead('>')) {
tag.ClosingBracket = ">";
@ -296,7 +296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -296,7 +296,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
if (TryPeek('<')) break;
if (HasMoreData()) {
tag.Attributes.Add(ReadAttribulte(tag));
tag.AddChild(ReadAttribulte());
continue;
}
break;
@ -308,14 +308,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -308,14 +308,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return tag;
}
RawText ReadWhiteSpace(RawObject parent)
RawText ReadWhiteSpace()
{
Debug.Assert(HasMoreData() && IsWhiteSpace() == true);
RawText ws = ReadFromCache<RawText>(currentLocation);
if (ws != null) return ws;
ws = new RawText() { Parent = parent };
ws = new RawText();
ws.StartOffset = currentLocation;
int start = currentLocation;
@ -327,14 +327,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -327,14 +327,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return ws;
}
RawAttribute ReadAttribulte(RawObject parent)
RawAttribute ReadAttribulte()
{
Debug.Assert(HasMoreData());
RawAttribute attr = ReadFromCache<RawAttribute>(currentLocation);
if (attr != null) return attr;
attr = new RawAttribute() { Parent = parent };
attr = new RawAttribute();
attr.StartOffset = currentLocation;
if (HasMoreData()) {
@ -346,10 +346,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -346,10 +346,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser
}
int checkpoint = currentLocation;
attr.EqualsSign = string.Empty;
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value;
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value;
if (TryRead('=')) {
attr.EqualsSign += "=";
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace(attr).Value;
if (IsWhiteSpace() == true) attr.EqualsSign += ReadWhiteSpace().Value;
if (IsWhiteSpaceOrReserved() == false) {
// Read attribute value
int start = currentLocation;
@ -375,14 +375,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser @@ -375,14 +375,14 @@ namespace ICSharpCode.AvalonEdit.XmlParser
return attr;
}
RawText ReadCharacterData(RawObject parent)
RawText ReadCharacterData()
{
Debug.Assert(HasMoreData());
RawText charData = ReadFromCache<RawText>(currentLocation);
if (charData != null) return charData;
charData = new RawText() { Parent = parent };
charData = new RawText();
charData.StartOffset = currentLocation;
int start = currentLocation;

Loading…
Cancel
Save