From 080f1697e97e13461ec6df4d31c8924d01257a1b Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Tue, 9 Apr 2019 01:47:48 +0300 Subject: MERGE --- .../Tango.Scripting.Editors/Xml/AXmlObject.cs | 266 +++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs') diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs new file mode 100644 index 000000000..4951a7c0b --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs @@ -0,0 +1,266 @@ +// 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 +{ + /// + /// Abstact base class for all types + /// + public abstract class AXmlObject: TextSegment + { + /// Empty string. The namespace used if there is no "xmlns" specified + public static readonly string NoNamespace = string.Empty; + + /// Namespace for "xml:" prefix: "http://www.w3.org/XML/1998/namespace" + public static readonly string XmlNamespace = "http://www.w3.org/XML/1998/namespace"; + + /// Namesapce for "xmlns:" prefix: "http://www.w3.org/2000/xmlns/" + public static readonly string XmlnsNamespace = "http://www.w3.org/2000/xmlns/"; + + /// Parent node. + /// + /// New cached items start with null parent. + /// Cache constraint: + /// If cached item has parent set, then the whole subtree must be consistent + /// + public AXmlObject Parent { get; set; } + + /// + /// Gets the document that has owns this object. + /// Once set, it is not changed. Not even set to null. + /// + internal AXmlDocument Document { get; set; } + + /// Creates new object + protected AXmlObject() + { + this.LastUpdatedFrom = this; + } + + /// Occurs before the value of any local properties changes. Nested changes do not cause the event to occur + public event EventHandler Changing; + + /// Occurs after the value of any local properties changed. Nested changes do not cause the event to occur + public event EventHandler Changed; + + /// Raises Changing event + protected void OnChanging() + { + AXmlParser.Log("Changing {0}", this); + if (Changing != null) { + Changing(this, new AXmlObjectEventArgs() { Object = this } ); + } + AXmlDocument doc = this.Document; + if (doc != null) { + doc.OnObjectChanging(this); + } + // As a convenience, also rasie an event for the parent element + AXmlTag me = this as AXmlTag; + if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) { + me.Parent.OnChanging(); + } + } + + /// Raises Changed event + protected void OnChanged() + { + AXmlParser.Log("Changed {0}", this); + if (Changed != null) { + Changed(this, new AXmlObjectEventArgs() { Object = this } ); + } + AXmlDocument doc = this.Document; + if (doc != null) { + doc.OnObjectChanged(this); + } + // As a convenience, also rasie an event for the parent element + AXmlTag me = this as AXmlTag; + if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) { + me.Parent.OnChanged(); + } + } + + List syntaxErrors; + + /// + /// The error that occured in the context of this node (excluding nested nodes) + /// + public IEnumerable MySyntaxErrors { + get { + if (syntaxErrors == null) { + return new SyntaxError[] {}; + } else { + return syntaxErrors; + } + } + } + + /// + /// The error that occured in the context of this node and all nested nodes. + /// It has O(n) cost. + /// + public IEnumerable SyntaxErrors { + get { + return GetSelfAndAllChildren().SelectMany(obj => obj.MySyntaxErrors); + } + } + + internal void AddSyntaxError(SyntaxError error) + { + DebugAssert(error.Object == this, "Must own the error"); + if (this.syntaxErrors == null) this.syntaxErrors = new List(); + syntaxErrors.Add(error); + } + + /// Throws exception if condition is false + /// Present in release mode - use only for very cheap aserts + protected static void Assert(bool condition, string message) + { + if (!condition) { + throw new InternalException("Assertion failed: " + message); + } + } + + /// Throws exception if condition is false + [Conditional("DEBUG")] + protected static void DebugAssert(bool condition, string message) + { + if (!condition) { + throw new InternalException("Assertion failed: " + message); + } + } + + /// Recursively gets self and all nested nodes. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")] + public virtual IEnumerable GetSelfAndAllChildren() + { + return new AXmlObject[] { this }; + } + + /// Get all ancestors of this node + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")] + public IEnumerable GetAncestors() + { + AXmlObject curr = this.Parent; + while(curr != null) { + yield return curr; + curr = curr.Parent; + } + } + + /// Call appropriate visit method on the given visitor + public abstract void AcceptVisitor(IAXmlVisitor visitor); + + /// The parser tree object this object was updated from + /// Initialized to 'this' + internal AXmlObject LastUpdatedFrom { get; private set; } + + internal bool IsCached { get; set; } + + /// Is call to UpdateDataFrom is allowed? + internal bool CanUpdateDataFrom(AXmlObject source) + { + return + this.GetType() == source.GetType() && + this.StartOffset == source.StartOffset && + (this.LastUpdatedFrom == source || !this.IsCached); + } + + /// Copy all data from the 'source' to this object + /// Returns true if any updates were done + internal virtual bool UpdateDataFrom(AXmlObject source) + { + Assert(this.GetType() == source.GetType(), "Source has different type"); + DebugAssert(this.StartOffset == source.StartOffset, "Source has different StartOffset"); + + if (this.LastUpdatedFrom == source) { + DebugAssert(this.EndOffset == source.EndOffset, "Source has different EndOffset"); + return false; + } + + Assert(!this.IsCached, "Can not update cached item"); + Assert(source.IsCached, "Must update from cache"); + + this.LastUpdatedFrom = source; + this.StartOffset = source.StartOffset; + // In some cases we are just updating objects of that same + // type and position and hoping to be luckily right + this.EndOffset = source.EndOffset; + + // Do not bother comparing - assume changed if non-null + if (this.syntaxErrors != null || source.syntaxErrors != null) { + // May be called again in derived class - oh, well, does not matter + OnChanging(); + this.Document.Parser.TrackedSegments.RemoveSyntaxErrorsOf(this); + if (source.syntaxErrors == null) { + this.syntaxErrors = null; + } else { + this.syntaxErrors = new List(); + foreach(var error in source.MySyntaxErrors) { + // The object differs, so create our own copy + // The source still might need it in the future and we do not want to break it + this.AddSyntaxError(error.Clone(this)); + } + } + this.Document.Parser.TrackedSegments.AddSyntaxErrorsOf(this); + OnChanged(); + } + + return true; + } + + /// Verify that the item is consistent. Only in debug build. + [Conditional("DEBUG")] + internal virtual void DebugCheckConsistency(bool allowNullParent) + { + + } + + /// + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0}({1}-{2})", this.GetType().Name.Remove(0, 4), this.StartOffset, this.EndOffset); + } + + #region Helpper methods + + /// The part of name before ":" + /// Empty string if not found + protected static string GetNamespacePrefix(string name) + { + if (string.IsNullOrEmpty(name)) return string.Empty; + 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) + { + if (string.IsNullOrEmpty(name)) return string.Empty; + int colonIndex = name.IndexOf(':'); + if (colonIndex != -1) { + return name.Remove(0, colonIndex + 1); + } else { + return name ?? string.Empty; + } + } + + #endregion + } +} -- cgit v1.3.1