| File: System\Xml\Linq\XObject.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml.Linq\src\System.Private.Xml.Linq.csproj (System.Private.Xml.Linq) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using Debug = System.Diagnostics.Debug; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; namespace System.Xml.Linq { /// <summary> /// Represents a node or an attribute in an XML tree. /// </summary> public abstract class XObject : IXmlLineInfo { internal XContainer? parent; internal object? annotations; internal XObject() { } /// <summary> /// Get the BaseUri for this <see cref="XObject"/>. /// </summary> public string BaseUri { get { XObject? o = this; while (true) { while (o != null && o.annotations == null) { o = o.parent; } if (o == null) break; BaseUriAnnotation? a = o.Annotation<BaseUriAnnotation>(); if (a != null) return a.baseUri; o = o.parent; } return string.Empty; } } /// <summary> /// Gets the XDocument object for this <see cref="XObject"/>. /// </summary> public XDocument? Document { get { XObject n = this; while (n.parent != null) n = n.parent; XDocument? doc = n as XDocument; return doc; } } /// <summary> /// Gets the node type for this <see cref="XObject"/>. /// </summary> public abstract XmlNodeType NodeType { get; } /// <summary> /// Gets the parent <see cref="XElement"/> of this <see cref="XObject"/>. /// </summary> /// <remarks> /// If this <see cref="XObject"/> has no parent <see cref="XElement"/>, this property returns null. /// </remarks> public XElement? Parent { get { return parent as XElement; } } /// <summary> /// Adds an object to the annotation list of this <see cref="XObject"/>. /// </summary> /// <param name="annotation">The annotation to add.</param> public void AddAnnotation(object annotation) { ArgumentNullException.ThrowIfNull(annotation); if (annotations == null) { annotations = annotation is object[]? new object[] { annotation } : annotation; } else { object?[]? a = annotations as object[]; if (a == null) { annotations = new object[] { annotations, annotation }; } else { int i = 0; while (i < a.Length && a[i] != null) i++; if (i == a.Length) { Array.Resize(ref a, i * 2); annotations = a; } a[i] = annotation; } } } /// <summary> /// Returns the first annotation object of the specified type from the list of annotations /// of this <see cref="XObject"/>. /// </summary> /// <param name="type">The type of the annotation to retrieve.</param> /// <returns> /// The first matching annotation object, or null /// if no annotation is the specified type. /// </returns> public object? Annotation(Type type) { ArgumentNullException.ThrowIfNull(type); if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { if (XHelper.IsInstanceOfType(annotations, type)) return annotations; } else { for (int i = 0; i < a.Length; i++) { object? obj = a[i]; if (obj == null) break; if (XHelper.IsInstanceOfType(obj, type)) return obj; } } } return null; } private object? AnnotationForSealedType(Type type) { Debug.Assert(type != null); if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { if (annotations.GetType() == type) return annotations; } else { for (int i = 0; i < a.Length; i++) { object? obj = a[i]; if (obj == null) break; if (obj.GetType() == type) return obj; } } } return null; } /// <summary> /// Returns the first annotation object of the specified type from the list of annotations /// of this <see cref="XObject"/>. /// </summary> /// <typeparam name="T">The type of the annotation to retrieve.</typeparam> /// <returns> /// The first matching annotation object, or null if no annotation /// is the specified type. /// </returns> public T? Annotation<T>() where T : class { if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) return annotations as T; for (int i = 0; i < a.Length; i++) { object? obj = a[i]; if (obj == null) break; T? result = obj as T; if (result != null) return result; } } return null; } /// <summary> /// Returns an enumerable collection of annotations of the specified type /// for this <see cref="XObject"/>. /// </summary> /// <param name="type">The type of the annotations to retrieve.</param> /// <returns>An enumerable collection of annotations for this XObject.</returns> public IEnumerable<object> Annotations(Type type) { ArgumentNullException.ThrowIfNull(type); return AnnotationsIterator(type); } private IEnumerable<object> AnnotationsIterator(Type type) { if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { if (XHelper.IsInstanceOfType(annotations, type)) yield return annotations; } else { for (int i = 0; i < a.Length; i++) { object? obj = a[i]; if (obj == null) break; if (XHelper.IsInstanceOfType(obj, type)) yield return obj; } } } } /// <summary> /// Returns an enumerable collection of annotations of the specified type /// for this <see cref="XObject"/>. /// </summary> /// <typeparam name="T">The type of the annotations to retrieve.</typeparam> /// <returns>An enumerable collection of annotations for this XObject.</returns> public IEnumerable<T> Annotations<T>() where T : class { if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { T? result = annotations as T; if (result != null) yield return result; } else { for (int i = 0; i < a.Length; i++) { object? obj = a[i]; if (obj == null) break; T? result = obj as T; if (result != null) yield return result; } } } } /// <summary> /// Removes the annotations of the specified type from this <see cref="XObject"/>. /// </summary> /// <param name="type">The type of annotations to remove.</param> public void RemoveAnnotations(Type type) { ArgumentNullException.ThrowIfNull(type); if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { if (XHelper.IsInstanceOfType(annotations, type)) annotations = null; } else { int i = 0, j = 0; while (i < a.Length) { object? obj = a[i]; if (obj == null) break; if (!XHelper.IsInstanceOfType(obj, type)) a[j++] = obj; i++; } if (j == 0) { annotations = null; } else { while (j < i) a[j++] = null; } } } } /// <summary> /// Removes the annotations of the specified type from this <see cref="XObject"/>. /// </summary> /// <typeparam name="T">The type of annotations to remove.</typeparam> public void RemoveAnnotations<T>() where T : class { if (annotations != null) { object?[]? a = annotations as object[]; if (a == null) { if (annotations is T) annotations = null; } else { int i = 0, j = 0; while (i < a.Length) { object? obj = a[i]; if (obj == null) break; if (!(obj is T)) a[j++] = obj; i++; } if (j == 0) { annotations = null; } else { while (j < i) a[j++] = null; } } } } /// <summary> /// Occurs when this <see cref="XObject"/> or any of its descendants have changed. /// </summary> public event EventHandler<XObjectChangeEventArgs> Changed { add { if (value == null) return; XObjectChangeAnnotation? a = Annotation<XObjectChangeAnnotation>(); if (a == null) { a = new XObjectChangeAnnotation(); AddAnnotation(a); } a.changed += value; } remove { if (value == null) return; XObjectChangeAnnotation? a = Annotation<XObjectChangeAnnotation>(); if (a == null) return; a.changed -= value; if (a.changing == null && a.changed == null) { RemoveAnnotations<XObjectChangeAnnotation>(); } } } /// <summary> /// Occurs when this <see cref="XObject"/> or any of its descendants are about to change. /// </summary> public event EventHandler<XObjectChangeEventArgs> Changing { add { if (value == null) return; XObjectChangeAnnotation? a = Annotation<XObjectChangeAnnotation>(); if (a == null) { a = new XObjectChangeAnnotation(); AddAnnotation(a); } a.changing += value; } remove { if (value == null) return; XObjectChangeAnnotation? a = Annotation<XObjectChangeAnnotation>(); if (a == null) return; a.changing -= value; if (a.changing == null && a.changed == null) { RemoveAnnotations<XObjectChangeAnnotation>(); } } } bool IXmlLineInfo.HasLineInfo() { return Annotation<LineInfoAnnotation>() != null; } int IXmlLineInfo.LineNumber { get { LineInfoAnnotation? a = Annotation<LineInfoAnnotation>(); if (a != null) return a.lineNumber; return 0; } } int IXmlLineInfo.LinePosition { get { LineInfoAnnotation? a = Annotation<LineInfoAnnotation>(); if (a != null) return a.linePosition; return 0; } } internal bool HasBaseUri { get { return Annotation<BaseUriAnnotation>() != null; } } internal bool NotifyChanged(object sender, XObjectChangeEventArgs e) { bool notify = false; XObject? o = this; while (true) { while (o != null && o.annotations == null) { o = o.parent; } if (o == null) break; XObjectChangeAnnotation? a = o.Annotation<XObjectChangeAnnotation>(); if (a != null) { notify = true; a.changed?.Invoke(sender, e); } o = o.parent; } return notify; } internal bool NotifyChanging(object sender, XObjectChangeEventArgs e) { bool notify = false; XObject? o = this; while (true) { while (o != null && o.annotations == null) { o = o.parent; } if (o == null) break; XObjectChangeAnnotation? a = o.Annotation<XObjectChangeAnnotation>(); if (a != null) { notify = true; a.changing?.Invoke(sender, e); } o = o.parent; } return notify; } internal void SetBaseUri(string baseUri) { AddAnnotation(new BaseUriAnnotation(baseUri)); } internal void SetLineInfo(int lineNumber, int linePosition) { AddAnnotation(new LineInfoAnnotation(lineNumber, linePosition)); } internal bool SkipNotify() { XObject? o = this; while (true) { while (o != null && o.annotations == null) { o = o.parent; } if (o == null) return true; if (o.Annotation<XObjectChangeAnnotation>() != null) return false; o = o.parent; } } /// <summary> /// Walks the tree starting with "this" node and returns first annotation of type <see cref="SaveOptions"/> /// found in the ancestors. /// </summary> /// <returns>The effective <see cref="SaveOptions"/> for this <see cref="XObject"/></returns> internal SaveOptions GetSaveOptionsFromAnnotations() { XObject? o = this; while (true) { while (o != null && o.annotations == null) { o = o.parent; } if (o == null) { return SaveOptions.None; } object? saveOptions = o.AnnotationForSealedType(typeof(SaveOptions)); if (saveOptions != null) { return (SaveOptions)saveOptions; } o = o.parent; } } } }