Browse Source

XML Parser: Added some helper methods

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4617 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 16 years ago
parent
commit
3145161f29
  1. 4
      samples/XmlDOM/Window1.xaml
  2. 19
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs
  3. 225
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/RawObjects.cs
  4. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs
  5. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/XmlParser.cs

4
samples/XmlDOM/Window1.xaml

@ -9,10 +9,10 @@
<Storyboard x:Key="anim"> <Storyboard x:Key="anim">
<ColorAnimation Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)" To="Transparent" Duration="0:0:4"/> <ColorAnimation Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)" To="Transparent" Duration="0:0:4"/>
</Storyboard> </Storyboard>
<HierarchicalDataTemplate DataType="{x:Type clr:RawDocument}" ItemsSource="{Binding Helper_Elements}"> <HierarchicalDataTemplate DataType="{x:Type clr:RawDocument}" ItemsSource="{Binding Elements}">
<TextBlock Text="XML Document" Margin="2"/> <TextBlock Text="XML Document" Margin="2"/>
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type clr:RawElement}" ItemsSource="{Binding Helper_AttributesAndElements}"> <HierarchicalDataTemplate DataType="{x:Type clr:RawElement}" ItemsSource="{Binding AttributesAndElements}">
<TextBlock Text="{Binding StartTag.Name}" Margin="2" Initialized="BindElement"/> <TextBlock Text="{Binding StartTag.Name}" Margin="2" Initialized="BindElement"/>
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type clr:RawAttribute}"> <HierarchicalDataTemplate DataType="{x:Type clr:RawAttribute}">

19
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Collections.cs

@ -72,15 +72,18 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
/// <summary> /// <summary>
/// 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).
/// </summary> /// </summary>
public class FilteredCollection<C, T>: ObservableCollection<T> where C: INotifyCollectionChanged, IList<T> public class FilteredCollection<T, C>: ObservableCollection<T> where C: INotifyCollectionChanged, IList
{ {
C source; C source;
Predicate<T> condition; Predicate<object> condition;
List<int> srcPtrs = new List<int>(); // Index to the original collection List<int> srcPtrs = new List<int>(); // Index to the original collection
public FilteredCollection(C source, Predicate<T> condition) public FilteredCollection(C source) : this (source, x => true) { }
public FilteredCollection(C source, Predicate<object> condition)
{ {
this.source = source; this.source = source;
this.condition = condition; this.condition = condition;
@ -95,8 +98,8 @@ namespace ICSharpCode.AvalonEdit.XmlParser
this.Clear(); this.Clear();
srcPtrs.Clear(); srcPtrs.Clear();
for(int i = 0; i < source.Count; i++) { for(int i = 0; i < source.Count; i++) {
if (condition(source[i])) { if (source[i] is T && condition(source[i])) {
this.Add(source[i]); this.Add((T)source[i]);
srcPtrs.Add(i); srcPtrs.Add(i);
} }
} }
@ -117,7 +120,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
if (addIndex == -1) addIndex = this.Count; if (addIndex == -1) addIndex = this.Count;
// Add items to collection // Add items to collection
for(int i = 0; i < e.NewItems.Count; i++) { 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]); this.InsertItem(addIndex, (T)e.NewItems[i]);
srcPtrs.Insert(addIndex, e.NewStartingIndex + i); srcPtrs.Insert(addIndex, e.NewStartingIndex + i);
addIndex++; addIndex++;
@ -154,7 +157,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// <summary> /// <summary>
/// Two collections in sequence /// Two collections in sequence
/// </summary> /// </summary>
public class MergedCollection<C, T>: ObservableCollection<T> where C: INotifyCollectionChanged, IList<T> public class MergedCollection<T, C>: ObservableCollection<T> where C: INotifyCollectionChanged, IList<T>
{ {
C a; C a;
C b; C b;

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

@ -165,6 +165,58 @@ namespace ICSharpCode.AvalonEdit.XmlParser
namesapce = XmlConvert.EncodeLocalName(namesapce); namesapce = XmlConvert.EncodeLocalName(namesapce);
return XName.Get(name, namesapce); return XName.Get(name, namesapce);
} }
#region Helpper methods
/// <summary> The part of name before ":". Empty string if not found </summary>
protected static string GetNamespacePrefix(string name)
{
int colonIndex = name.IndexOf(':');
if (colonIndex != -1) {
return name.Substring(0, colonIndex);
} else {
return string.Empty;
}
}
/// <summary> The part of name after ":". Whole name if not found </summary>
protected static string GetLocalName(string name)
{
int colonIndex = name.IndexOf(':');
if (colonIndex != -1) {
return name.Remove(0, colonIndex + 1);
} else {
return name ?? string.Empty;
}
}
/// <summary> Remove quoting from the given string </summary>
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
} }
/// <summary> /// <summary>
@ -183,12 +235,22 @@ namespace ICSharpCode.AvalonEdit.XmlParser
this.Children = new ChildrenCollection<RawObject>(); this.Children = new ChildrenCollection<RawObject>();
} }
public ObservableCollection<RawObject> Helper_Elements { #region Helpper methods
ObservableCollection<RawElement> elements;
/// <summary> Gets direcly nested elements (non-recursive) </summary>
public ObservableCollection<RawElement> Elements {
get { get {
return new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.Children, x => x is RawElement); if (elements == null) {
elements = new FilteredCollection<RawElement, ChildrenCollection<RawObject>>(this.Children);
}
return elements;
} }
} }
#endregion
public override void UpdateDataFrom(RawObject source) public override void UpdateDataFrom(RawObject source)
{ {
if (this.ReadCallID == source.ReadCallID) return; if (this.ReadCallID == source.ReadCallID) return;
@ -576,15 +638,101 @@ namespace ICSharpCode.AvalonEdit.XmlParser
} }
} }
public ObservableCollection<RawObject> Helper_AttributesAndElements { #region Helpper methods
ObservableCollection<RawAttribute> attributes;
/// <summary> Gets attributes of the element </summary>
public ObservableCollection<RawAttribute> Attributes {
get { get {
return new MergedCollection<ObservableCollection<RawObject>, RawObject>( if (attributes == null) {
new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.StartTag.Children, x => x is RawAttribute), attributes = new FilteredCollection<RawAttribute, ChildrenCollection<RawObject>>(this.StartTag.Children);
new FilteredCollection<ChildrenCollection<RawObject>, RawObject>(this.Children, x => x is RawElement) }
); return attributes;
} }
} }
ObservableCollection<RawObject> attributesAndElements;
/// <summary> Gets both attributes and elements </summary>
public ObservableCollection<RawObject> AttributesAndElements {
get {
if (attributesAndElements == null) {
attributesAndElements = new MergedCollection<RawObject, ObservableCollection<RawObject>> (
// New wrapper with RawObject types
new FilteredCollection<RawObject, ChildrenCollection<RawObject>>(this.StartTag.Children, x => x is RawAttribute),
new FilteredCollection<RawObject, ChildrenCollection<RawObject>>(this.Children, x => x is RawElement)
);
}
return attributesAndElements;
}
}
/// <summary> The part of name before ":". Empty string if not found </summary>
public string NamespacePrefix {
get {
return GetNamespacePrefix(this.StartTag.Name);
}
}
/// <summary> The part of name after ":". Whole name if not found </summary>
public string LocalName {
get {
return GetLocalName(this.StartTag.Name);
}
}
/// <summary> Resolved namespace of the name. String empty if not found </summary>
public string Namespace {
get {
return ResloveNamespacePrefix(this.NamespacePrefix);
}
}
/// <summary>
/// Recursively resolve given prefix in this context.
/// If prefix is empty find "xmlns" namespace.
/// </summary>
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
}
/// <summary>
/// Get unqoted value of attribute or null if not found.
/// It will match the local name in any namesapce.
/// </summary>
public string GetAttributeValue(string localName)
{
return GetAttributeValue(null, localName);
}
/// <summary>
/// Get unqoted value of attribute or null if not found
/// </summary>
/// <param name="namespace">Namespace. Empty stirng to match default. Null to match any.</param>
/// <param name="localName">Local name - text after ":"</param>
/// <returns></returns>
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) public override void AcceptVisitor(IXmlVisitor visitor)
{ {
visitor.VisitElement(this); visitor.VisitElement(this);
@ -660,11 +808,58 @@ namespace ICSharpCode.AvalonEdit.XmlParser
/// </summary> /// </summary>
public class RawAttribute: RawObject public class RawAttribute: RawObject
{ {
/// <summary> The raw name - exactly as in source file </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> Equals sign and surrounding whitespace </summary>
public string EqualsSign { get; set; } public string EqualsSign { get; set; }
public string Value { get; set; } /// <summary> The raw value - exactly as in source file (*probably* quoted) </summary>
public string QuotedValue { get; set; }
#region Helpper methods
// TODO: Provide method to dereference Value - & /// <summary> The part of name before ":". Empty string if not found </summary>
public string NamespacePrefix {
get {
return GetNamespacePrefix(this.Name);
}
}
/// <summary> The part of name after ":". Whole name if not found </summary>
public string LocalName {
get {
return GetLocalName(this.Name);
}
}
/// <summary> Resolved namespace of the name. String empty if not found </summary>
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
}
}
/// <summary> Attribute is declaring namespace ("xmlns" or "xmlns:*") </summary>
public bool IsNamespaceDeclaration {
get {
return this.Name == "xmlns" || this.NamespacePrefix == "xmlns";
}
}
/// <summary> Unquoted value of the attribute </summary>
public string Value {
get {
return Unquote(this.QuotedValue);
}
}
#endregion
public override void AcceptVisitor(IXmlVisitor visitor) public override void AcceptVisitor(IXmlVisitor visitor)
{ {
@ -678,11 +873,11 @@ namespace ICSharpCode.AvalonEdit.XmlParser
RawAttribute src = (RawAttribute)source; RawAttribute src = (RawAttribute)source;
if (this.Name != src.Name || if (this.Name != src.Name ||
this.EqualsSign != src.EqualsSign || this.EqualsSign != src.EqualsSign ||
this.Value != src.Value) this.QuotedValue != src.QuotedValue)
{ {
this.Name = src.Name; this.Name = src.Name;
this.EqualsSign = src.EqualsSign; this.EqualsSign = src.EqualsSign;
this.Value = src.Value; this.QuotedValue = src.QuotedValue;
OnChanged(); OnChanged();
} }
} }
@ -692,7 +887,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public XAttribute GetXAttribute() public XAttribute GetXAttribute()
{ {
if (xAttr == null) { 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 = new XAttribute(EncodeXName(this.Name), string.Empty);
xAttr.AddAnnotation(this); xAttr.AddAnnotation(this);
bool deleted = false; bool deleted = false;
@ -704,10 +899,10 @@ namespace ICSharpCode.AvalonEdit.XmlParser
void UpdateXAttribute(bool firstUpdate, ref bool deleted) 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)) { if (xAttr.Name == EncodeXName(this.Name)) {
xAttr.Value = this.Value ?? string.Empty; xAttr.Value = this.QuotedValue ?? string.Empty;
} else { } else {
XElement xParent = xAttr.Parent; XElement xParent = xAttr.Parent;
if (xAttr.Parent != null) xAttr.Remove(); // Duplicate items are not added if (xAttr.Parent != null) xAttr.Remove(); // Duplicate items are not added
@ -719,7 +914,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
public override string ToString() 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);
} }
} }

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/XmlParser/Visitors.cs

@ -109,7 +109,7 @@ namespace ICSharpCode.AvalonEdit.XmlParser
{ {
sb.Append(attribute.Name); sb.Append(attribute.Name);
sb.Append(attribute.EqualsSign); sb.Append(attribute.EqualsSign);
sb.Append(attribute.Value); sb.Append(attribute.QuotedValue);
} }
/// <summary> Visit RawText </summary> /// <summary> Visit RawText </summary>

2
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"); OnSyntaxError(attr, valueStart, currentLocation, "Attribute value must be quoted");
} }
} }
attr.Value = GetText(start, currentLocation); attr.QuotedValue = GetText(start, currentLocation);
attr.EndOffset = currentLocation; attr.EndOffset = currentLocation;

Loading…
Cancel
Save