// Copyright (c) 2009-2013 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Xml; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.Xml { /// /// XML object. Base class for all nodes in the XML document. /// public abstract class AXmlObject : ISegment { /// Empty string. The namespace used if there is no "xmlns" specified internal 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/"; readonly AXmlObject parent; internal readonly int startOffset; internal readonly InternalObject internalObject; IList children; internal AXmlObject(AXmlObject parent, int startOffset, InternalObject internalObject) { this.parent = parent; this.startOffset = startOffset; this.internalObject = internalObject; } /// /// Creates an XML reader that reads from this document. /// /// /// The reader will ignore comments and processing instructions; and will not have line information. /// public XmlReader CreateReader() { return new AXmlReader(CreateIteratorForReader()); } /// /// Creates an XML reader that reads from this document. /// /// Reader settings. /// Currently, only IgnoreComments is supported. /// /// The reader will not have line information. /// public XmlReader CreateReader(XmlReaderSettings settings) { return new AXmlReader(CreateIteratorForReader(), settings); } /// /// Creates an XML reader that reads from this document. /// /// Reader settings. /// Currently, only IgnoreComments is supported. /// /// The document that was used to parse the XML. It is used to convert offsets to line information. /// public XmlReader CreateReader(XmlReaderSettings settings, IDocument document) { if (document == null) throw new ArgumentNullException("document"); return new AXmlReader(CreateIteratorForReader(), settings, document.GetLocation); } /// /// Creates an XML reader that reads from this document. /// /// Reader settings. /// Currently, only IgnoreComments is supported. /// /// A function for converting offsets to line information. /// public XmlReader CreateReader(XmlReaderSettings settings, Func offsetToTextLocation) { return new AXmlReader(CreateIteratorForReader(), settings, offsetToTextLocation); } internal virtual ObjectIterator CreateIteratorForReader() { return new ObjectIterator(new[] { internalObject }, startOffset); } /// /// Gets the parent node. /// public AXmlObject Parent { get { return parent; } } /// /// Gets the list of child objects. /// public IList Children { get { var result = LazyInit.VolatileRead(ref this.children); if (result != null) { return result; } else { if (internalObject.NestedObjects != null) { var array = new AXmlObject[internalObject.NestedObjects.Length]; for (int i = 0; i < array.Length; i++) { array[i] = internalObject.NestedObjects[i].CreatePublicObject(this, startOffset); } result = Array.AsReadOnly(array); } else { result = EmptyList.Instance; } return LazyInit.GetOrSet(ref this.children, result); } } } /// /// Gets a child fully containg the given offset. /// Goes recursively down the tree. /// Special case if at the end of attribute or text /// public AXmlObject GetChildAtOffset(int offset) { foreach(AXmlObject child in this.Children) { if (offset == child.EndOffset && (child is AXmlAttribute || child is AXmlText)) return child; if (child.StartOffset < offset && offset < child.EndOffset) { return child.GetChildAtOffset(offset); } } return this; // No children at offset } /// /// The error that occured in the context of this node (excluding nested nodes) /// public IEnumerable MySyntaxErrors { get { if (internalObject.SyntaxErrors != null) { return internalObject.SyntaxErrors.Select(e => new SyntaxError(startOffset + e.RelativeStart, startOffset + e.RelativeEnd, e.Description)); } else { return EmptyList.Instance; } } } /// /// The error that occured in the context of this node and all nested nodes. /// It has O(n) cost. /// public IEnumerable SyntaxErrors { get { return TreeTraversal.PreOrder(this, n => n.Children).SelectMany(obj => obj.MySyntaxErrors); } } /// Get all ancestors of this node public IEnumerable Ancestors { get { AXmlObject curr = this.Parent; while(curr != null) { yield return curr; curr = curr.Parent; } } } #region Helper methods /// The part of name before ":" /// Empty string if not found internal 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 internal 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 /// Call appropriate visit method on the given visitor public abstract void AcceptVisitor(AXmlVisitor visitor); /// /// Gets the start offset of the segment. /// public int StartOffset { get { return startOffset; } } int ISegment.Offset { get { return startOffset; } } /// public int Length { get { return internalObject.Length; } } /// public int EndOffset { get { return startOffset + internalObject.Length; } } } }