diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-04-09 01:47:48 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-04-09 01:47:48 +0300 |
| commit | 080f1697e97e13461ec6df4d31c8924d01257a1b (patch) | |
| tree | b1fe0285de7bc9bc52e9e2195e66fe022bf8f5b3 /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs | |
| parent | 1608e69a417bc5e40a607c3958c4a60f19f66f1a (diff) | |
| download | Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.tar.gz Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.zip | |
MERGE
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs new file mode 100644 index 000000000..080d538e8 --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs @@ -0,0 +1,226 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +using Tango.Scripting.Editors.Document; + +namespace Tango.Scripting.Editors.Xml +{ + /// <summary> + /// Logical grouping of other nodes together. + /// </summary> + public class AXmlElement: AXmlContainer + { + /// <summary> No tags are missing anywhere within this element (recursive) </summary> + public bool IsProperlyNested { get; set; } + /// <returns> True in wellformed XML </returns> + public bool HasStartOrEmptyTag { get; set; } + /// <returns> True in wellformed XML </returns> + public bool HasEndTag { get; set; } + + /// <inheritdoc/> + internal override bool UpdateDataFrom(AXmlObject source) + { + if (!base.UpdateDataFrom(source)) return false; + AXmlElement src = (AXmlElement)source; + // Clear the cache for this - quite expensive + attributesAndElements = null; + if (this.IsProperlyNested != src.IsProperlyNested || + this.HasStartOrEmptyTag != src.HasStartOrEmptyTag || + this.HasEndTag != src.HasEndTag) + { + OnChanging(); + this.IsProperlyNested = src.IsProperlyNested; + this.HasStartOrEmptyTag = src.HasStartOrEmptyTag; + this.HasEndTag = src.HasEndTag; + OnChanged(); + return true; + } else { + return false; + } + } + + /// <summary> The start or empty-element tag if there is any </summary> + internal AXmlTag StartTag { + get { + Assert(HasStartOrEmptyTag, "Does not have a start tag"); + return (AXmlTag)this.Children[0]; + } + } + + /// <summary> The end tag if there is any </summary> + internal AXmlTag EndTag { + get { + Assert(HasEndTag, "Does not have an end tag"); + return (AXmlTag)this.Children[this.Children.Count - 1]; + } + } + + internal override void DebugCheckConsistency(bool checkParentPointers) + { + DebugAssert(Children.Count > 0, "No children"); + base.DebugCheckConsistency(checkParentPointers); + } + + #region Helpper methods + + /// <summary> Gets attributes of the element </summary> + /// <remarks> + /// Warning: this is a cenvenience method to access the attributes of the start tag. + /// However, since the start tag might be moved/replaced, this property might return + /// different values over time. + /// </remarks> + public AXmlAttributeCollection Attributes { + get { + if (this.HasStartOrEmptyTag) { + return this.StartTag.Attributes; + } else { + return AXmlAttributeCollection.Empty; + } + } + } + + ObservableCollection<AXmlObject> attributesAndElements; + + /// <summary> Gets both attributes and elements. Expensive, avoid use. </summary> + /// <remarks> Warning: the collection will regenerate after each update </remarks> + public ObservableCollection<AXmlObject> AttributesAndElements { + get { + if (attributesAndElements == null) { + if (this.HasStartOrEmptyTag) { + attributesAndElements = new MergedCollection<AXmlObject, ObservableCollection<AXmlObject>> ( + // New wrapper with RawObject types + new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.StartTag.Children, x => x is AXmlAttribute), + new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement) + ); + } else { + attributesAndElements = new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement); + } + } + return attributesAndElements; + } + } + + /// <summary> Name with namespace prefix - exactly as in source </summary> + public string Name { + get { + if (this.HasStartOrEmptyTag) { + return this.StartTag.Name; + } else { + return this.EndTag.Name; + } + } + } + + /// <summary> The part of name before ":" </summary> + /// <returns> Empty string if not found </returns> + public string Prefix { + get { + return GetNamespacePrefix(this.Name); + } + } + + /// <summary> The part of name after ":" </summary> + /// <returns> Empty string if not found </returns> + public string LocalName { + get { + return GetLocalName(this.Name); + } + } + + /// <summary> Resolved namespace of the name </summary> + /// <returns> Empty string if prefix is not found </returns> + public string Namespace { + get { + string prefix = this.Prefix; + if (string.IsNullOrEmpty(prefix)) { + return FindDefaultNamespace(); + } else { + return ResolvePrefix(prefix); + } + } + } + + /// <summary> Find the defualt namespace for this context </summary> + public string FindDefaultNamespace() + { + AXmlElement current = this; + while(current != null) { + string namesapce = current.GetAttributeValue(NoNamespace, "xmlns"); + if (namesapce != null) return namesapce; + current = current.Parent as AXmlElement; + } + return string.Empty; // No namesapce + } + + /// <summary> + /// Recursively resolve given prefix in this context. Prefix must have some value. + /// </summary> + /// <returns> Empty string if prefix is not found </returns> + public string ResolvePrefix(string prefix) + { + if (string.IsNullOrEmpty(prefix)) throw new ArgumentException("No prefix given", "prefix"); + + // Implicit namesapces + if (prefix == "xml") return XmlNamespace; + if (prefix == "xmlns") return XmlnsNamespace; + + AXmlElement current = this; + while(current != null) { + string namesapce = current.GetAttributeValue(XmlnsNamespace, prefix); + if (namesapce != null) return namesapce; + current = current.Parent as AXmlElement; + } + return NoNamespace; // Can not find prefix + } + + /// <summary> + /// Get unquoted value of attribute. + /// It looks in the no namespace (empty string). + /// </summary> + /// <returns>Null if not found</returns> + public string GetAttributeValue(string localName) + { + return GetAttributeValue(NoNamespace, localName); + } + + /// <summary> + /// Get unquoted value of attribute + /// </summary> + /// <param name="namespace">Namespace. Can be no namepace (empty string), which is the default for attributes.</param> + /// <param name="localName">Local name - text after ":"</param> + /// <returns>Null if not found</returns> + public string GetAttributeValue(string @namespace, string localName) + { + @namespace = @namespace ?? string.Empty; + foreach(AXmlAttribute attr in this.Attributes.GetByLocalName(localName)) { + DebugAssert(attr.LocalName == localName, "Bad hashtable"); + if (attr.Namespace == @namespace) { + return attr.Value; + } + } + return null; + } + + #endregion + + /// <inheritdoc/> + public override void AcceptVisitor(IAXmlVisitor visitor) + { + visitor.VisitElement(this); + } + + /// <inheritdoc/> + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}' Attr:{2} Chld:{3} Nest:{4}]", base.ToString(), this.Name, this.HasStartOrEmptyTag ? this.StartTag.Children.Count : 0, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad"); + } + } +} |
