From 3145161f29671f74fa29e3abf113740c9b8e57b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 8 Aug 2009 09:59:23 +0000 Subject: [PATCH] XML Parser: Added some helper methods git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4617 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- samples/XmlDOM/Window1.xaml | 4 +- .../XmlParser/Collections.cs | 19 +- .../XmlParser/RawObjects.cs | 225 ++++++++++++++++-- .../XmlParser/Visitors.cs | 2 +- .../XmlParser/XmlParser.cs | 2 +- 5 files changed, 225 insertions(+), 27 deletions(-) diff --git a/samples/XmlDOM/Window1.xaml b/samples/XmlDOM/Window1.xaml index 6fe699cc89..d9e44b3bd3 100644 --- a/samples/XmlDOM/Window1.xaml +++ b/samples/XmlDOM/Window1.xaml @@ -9,10 +9,10 @@ - + - + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs index 4ec287876e..ab75ca8ade 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs @@ -72,15 +72,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser } /// - /// Collection that presents only some items from the wrapped collection + /// Collection that presents only some items from the wrapped collection. + /// It implicitely filters object that are not of type T (or derived). /// - public class FilteredCollection: ObservableCollection where C: INotifyCollectionChanged, IList + public class FilteredCollection: ObservableCollection where C: INotifyCollectionChanged, IList { C source; - Predicate condition; + Predicate condition; List srcPtrs = new List(); // Index to the original collection - public FilteredCollection(C source, Predicate condition) + public FilteredCollection(C source) : this (source, x => true) { } + + public FilteredCollection(C source, Predicate condition) { this.source = source; this.condition = condition; @@ -95,8 +98,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser this.Clear(); srcPtrs.Clear(); for(int i = 0; i < source.Count; i++) { - if (condition(source[i])) { - this.Add(source[i]); + if (source[i] is T && condition(source[i])) { + this.Add((T)source[i]); srcPtrs.Add(i); } } @@ -117,7 +120,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser if (addIndex == -1) addIndex = this.Count; // Add items to collection for(int i = 0; i < e.NewItems.Count; i++) { - if (condition((T)e.NewItems[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++; @@ -154,7 +157,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser /// /// Two collections in sequence /// - public class MergedCollection: ObservableCollection where C: INotifyCollectionChanged, IList + public class MergedCollection: ObservableCollection where C: INotifyCollectionChanged, IList { C a; C b; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs index 684f001414..db3070e6b6 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs @@ -165,6 +165,58 @@ namespace ICSharpCode.AvalonEdit.XmlParser namesapce = XmlConvert.EncodeLocalName(namesapce); return XName.Get(name, namesapce); } + + #region Helpper methods + + /// The part of name before ":". Empty string if not found + protected static string GetNamespacePrefix(string name) + { + int colonIndex = name.IndexOf(':'); + if (colonIndex != -1) { + return name.Substring(0, colonIndex); + } else { + return string.Empty; + } + } + + /// The part of name after ":". Whole name if not found + protected static string GetLocalName(string name) + { + int colonIndex = name.IndexOf(':'); + if (colonIndex != -1) { + return name.Remove(0, colonIndex + 1); + } else { + return name ?? string.Empty; + } + } + + /// Remove quoting from the given string + protected static string Unquote(string quoted) + { + if (string.IsNullOrEmpty(quoted)) return string.Empty; + char first = quoted[0]; + if (quoted.Length == 1) return (first == '"' || first == '\'') ? string.Empty : quoted; + char last = quoted[quoted.Length - 1]; + if (first == '"' || first == '\'') { + if (first == last) { + // Remove both quotes + return quoted.Substring(1, quoted.Length - 2); + } else { + // Remove first quote + return quoted.Remove(0, 1); + } + } else { + if (last == '"' || last == '\'') { + // Remove last quote + return quoted.Substring(0, quoted.Length - 1); + } else { + // Keep whole string + return quoted; + } + } + } + + #endregion } /// @@ -183,12 +235,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser this.Children = new ChildrenCollection(); } - public ObservableCollection Helper_Elements { + #region Helpper methods + + ObservableCollection elements; + + /// Gets direcly nested elements (non-recursive) + public ObservableCollection Elements { get { - return new FilteredCollection, RawObject>(this.Children, x => x is RawElement); + if (elements == null) { + elements = new FilteredCollection>(this.Children); + } + return elements; } } + #endregion + public override void UpdateDataFrom(RawObject source) { if (this.ReadCallID == source.ReadCallID) return; @@ -576,15 +638,101 @@ namespace ICSharpCode.AvalonEdit.XmlParser } } - public ObservableCollection Helper_AttributesAndElements { + #region Helpper methods + + ObservableCollection attributes; + + /// Gets attributes of the element + public ObservableCollection Attributes { get { - return new MergedCollection, RawObject>( - new FilteredCollection, RawObject>(this.StartTag.Children, x => x is RawAttribute), - new FilteredCollection, RawObject>(this.Children, x => x is RawElement) - ); + if (attributes == null) { + attributes = new FilteredCollection>(this.StartTag.Children); + } + return attributes; } } + ObservableCollection attributesAndElements; + + /// Gets both attributes and elements + public ObservableCollection AttributesAndElements { + get { + if (attributesAndElements == null) { + attributesAndElements = new MergedCollection> ( + // New wrapper with RawObject types + new FilteredCollection>(this.StartTag.Children, x => x is RawAttribute), + new FilteredCollection>(this.Children, x => x is RawElement) + ); + } + return attributesAndElements; + } + } + + /// The part of name before ":". Empty string if not found + public string NamespacePrefix { + get { + return GetNamespacePrefix(this.StartTag.Name); + } + } + + /// The part of name after ":". Whole name if not found + public string LocalName { + get { + return GetLocalName(this.StartTag.Name); + } + } + + /// Resolved namespace of the name. String empty if not found + public string Namespace { + get { + return ResloveNamespacePrefix(this.NamespacePrefix); + } + } + + /// + /// Recursively resolve given prefix in this context. + /// If prefix is empty find "xmlns" namespace. + /// + public string ResloveNamespacePrefix(string prefix) + { + string definition = string.IsNullOrEmpty(prefix) ? "xmlns" : "xmlns:" + prefix; + RawElement current = this; + while(current != null) { + string namesapce = current.GetAttributeValue(definition); + if (namesapce != null) return namesapce; + current = current.Parent as RawElement; + } + return string.Empty; // Default or undefined + } + + /// + /// Get unqoted value of attribute or null if not found. + /// It will match the local name in any namesapce. + /// + public string GetAttributeValue(string localName) + { + return GetAttributeValue(null, localName); + } + + /// + /// Get unqoted value of attribute or null if not found + /// + /// Namespace. Empty stirng to match default. Null to match any. + /// Local name - text after ":" + /// + public string GetAttributeValue(string @namespace, string localName) + { + // TODO: More efficient + foreach(RawAttribute attr in this.Attributes) { + if (attr.LocalName == localName && (@namespace == null || attr.Namespace == @namespace)) { + return attr.Value; + } + } + return null; + } + + #endregion + public override void AcceptVisitor(IXmlVisitor visitor) { visitor.VisitElement(this); @@ -660,11 +808,58 @@ namespace ICSharpCode.AvalonEdit.XmlParser /// public class RawAttribute: RawObject { + /// The raw name - exactly as in source file public string Name { get; set; } + /// Equals sign and surrounding whitespace public string EqualsSign { get; set; } - public string Value { get; set; } + /// The raw value - exactly as in source file (*probably* quoted) + public string QuotedValue { get; set; } + + #region Helpper methods - // TODO: Provide method to dereference Value - & + /// The part of name before ":". Empty string if not found + public string NamespacePrefix { + get { + return GetNamespacePrefix(this.Name); + } + } + + /// The part of name after ":". Whole name if not found + public string LocalName { + get { + return GetLocalName(this.Name); + } + } + + /// Resolved namespace of the name. String empty if not found + public string Namespace { + get { + RawTag tag = this.Parent as RawTag; + if (tag != null) { + RawElement elem = tag.Parent as RawElement; + if (elem != null) { + return elem.ResloveNamespacePrefix(this.NamespacePrefix); + } + } + return string.Empty; // Orphaned + } + } + + /// Attribute is declaring namespace ("xmlns" or "xmlns:*") + public bool IsNamespaceDeclaration { + get { + return this.Name == "xmlns" || this.NamespacePrefix == "xmlns"; + } + } + + /// Unquoted value of the attribute + public string Value { + get { + return Unquote(this.QuotedValue); + } + } + + #endregion public override void AcceptVisitor(IXmlVisitor visitor) { @@ -678,11 +873,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser RawAttribute src = (RawAttribute)source; if (this.Name != src.Name || this.EqualsSign != src.EqualsSign || - this.Value != src.Value) + this.QuotedValue != src.QuotedValue) { this.Name = src.Name; this.EqualsSign = src.EqualsSign; - this.Value = src.Value; + this.QuotedValue = src.QuotedValue; OnChanged(); } } @@ -692,7 +887,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser public XAttribute GetXAttribute() { if (xAttr == null) { - LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.Value); + LogLinq("Creating XAttribute '{0}={1}'", this.Name, this.QuotedValue); xAttr = new XAttribute(EncodeXName(this.Name), string.Empty); xAttr.AddAnnotation(this); bool deleted = false; @@ -704,10 +899,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser void UpdateXAttribute(bool firstUpdate, ref bool deleted) { - if (!firstUpdate) LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.Value); + if (!firstUpdate) LogLinq("Updating XAttribute '{0}={1}'", this.Name, this.QuotedValue); if (xAttr.Name == EncodeXName(this.Name)) { - xAttr.Value = this.Value ?? string.Empty; + xAttr.Value = this.QuotedValue ?? string.Empty; } else { XElement xParent = xAttr.Parent; if (xAttr.Parent != null) xAttr.Remove(); // Duplicate items are not added @@ -719,7 +914,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser public override string ToString() { - return string.Format("[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value); + return string.Format("[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.QuotedValue); } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs index e4a604c9f1..df78a65952 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs @@ -109,7 +109,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser { sb.Append(attribute.Name); sb.Append(attribute.EqualsSign); - sb.Append(attribute.Value); + sb.Append(attribute.QuotedValue); } /// Visit RawText diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs index 0dd84d44a0..746c332ae0 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs @@ -896,7 +896,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser OnSyntaxError(attr, valueStart, currentLocation, "Attribute value must be quoted"); } } - attr.Value = GetText(start, currentLocation); + attr.QuotedValue = GetText(start, currentLocation); attr.EndOffset = currentLocation;