File: System\Xml\Dom\XmlAttribute.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Xml.Schema;
using System.Xml.XPath;
 
namespace System.Xml
{
    // Represents an attribute of the XMLElement object. Valid and default
    // values for the attribute are defined in a DTD or schema.
    public class XmlAttribute : XmlNode
    {
        private XmlName _name;
        private XmlLinkedNode? _lastChild;
 
        internal XmlAttribute(XmlName name, XmlDocument doc) : base(doc)
        {
            Debug.Assert(name != null);
            Debug.Assert(doc != null);
            this.parentNode = null;
            if (!doc.IsLoading)
            {
                XmlDocument.CheckName(name.Prefix);
                XmlDocument.CheckName(name.LocalName);
            }
            if (name.LocalName.Length == 0)
                throw new ArgumentException(SR.Xdom_Attr_Name);
            _name = name;
        }
 
        internal int LocalNameHash
        {
            get { return _name.HashCode; }
        }
 
        protected internal XmlAttribute(string? prefix, string localName, string? namespaceURI, XmlDocument doc)
        : this(doc.AddAttrXmlName(prefix, localName, namespaceURI, null), doc)
        {
        }
 
        internal XmlName XmlName
        {
            get { return _name; }
            set { _name = value; }
        }
 
        // Creates a duplicate of this node.
        public override XmlNode CloneNode(bool deep)
        {
            // CloneNode for attributes is deep irrespective of parameter 'deep' value
            Debug.Assert(OwnerDocument != null);
            XmlDocument doc = OwnerDocument;
            XmlAttribute attr = doc.CreateAttribute(Prefix, LocalName, NamespaceURI);
            attr.CopyChildren(doc, this, true);
            return attr;
        }
 
        // Gets the parent of this node (for nodes that can have parents).
        public override XmlNode? ParentNode
        {
            get { return null; }
        }
 
        // Gets the name of the node.
        public override string Name
        {
            get { return _name.Name; }
        }
 
        // Gets the name of the node without the namespace prefix.
        public override string LocalName
        {
            get { return _name.LocalName; }
        }
 
        // Gets the namespace URI of this node.
        public override string NamespaceURI
        {
            get { return _name.NamespaceURI; }
        }
 
        // Gets or sets the namespace prefix of this node.
        [AllowNull]
        public override string Prefix
        {
            get { return _name.Prefix; }
            set { _name = _name.OwnerDocument.AddAttrXmlName(value, LocalName, NamespaceURI, SchemaInfo); }
        }
 
        // Gets the type of the current node.
        public override XmlNodeType NodeType
        {
            get { return XmlNodeType.Attribute; }
        }
 
        // Gets the XmlDocument that contains this node.
        public override XmlDocument OwnerDocument
        {
            get
            {
                return _name.OwnerDocument;
            }
        }
 
        // Gets or sets the value of the node.
        [AllowNull]
        public override string Value
        {
            get { return InnerText; }
            set { InnerText = value!; } //use InnerText which has perf optimization
        }
 
        public override IXmlSchemaInfo SchemaInfo
        {
            get
            {
                return _name;
            }
        }
 
        public override string InnerText
        {
            set
            {
                if (PrepareOwnerElementInElementIdAttrMap())
                {
                    string innerText = base.InnerText;
                    base.InnerText = value;
                    ResetOwnerElementInElementIdAttrMap(innerText);
                }
                else
                {
                    base.InnerText = value;
                }
            }
        }
 
        internal bool PrepareOwnerElementInElementIdAttrMap()
        {
            XmlDocument ownerDocument = OwnerDocument;
            if (ownerDocument.DtdSchemaInfo != null)
            { // DTD exists
                XmlElement? ownerElement = OwnerElement;
                if (ownerElement != null)
                {
                    return ownerElement.Attributes.PrepareParentInElementIdAttrMap(Prefix, LocalName);
                }
            }
 
            return false;
        }
 
        internal void ResetOwnerElementInElementIdAttrMap(string oldInnerText)
        {
            XmlElement? ownerElement = OwnerElement;
            ownerElement?.Attributes.ResetParentInElementIdAttrMap(oldInnerText, InnerText);
        }
 
        internal override bool IsContainer
        {
            get { return true; }
        }
 
        //the function is provided only at Load time to speed up Load process
        internal override XmlNode AppendChildForLoad(XmlNode newChild, XmlDocument doc)
        {
            XmlNodeChangedEventArgs? args = doc.GetInsertEventArgsForLoad(newChild, this);
 
            if (args != null)
                doc.BeforeEvent(args);
 
            XmlLinkedNode newNode = (XmlLinkedNode)newChild;
 
            if (_lastChild == null)
            { // if LastNode == null
                newNode.next = newNode;
                _lastChild = newNode;
                newNode.SetParentForLoad(this);
            }
            else
            {
                XmlLinkedNode refNode = _lastChild; // refNode = LastNode;
                newNode.next = refNode.next;
                refNode.next = newNode;
                _lastChild = newNode; // LastNode = newNode;
                if (refNode.IsText
                    && newNode.IsText)
                {
                    NestTextNodes(refNode, newNode);
                }
                else
                {
                    newNode.SetParentForLoad(this);
                }
            }
 
            if (args != null)
                doc.AfterEvent(args);
 
            return newNode;
        }
 
        internal override XmlLinkedNode? LastNode
        {
            get { return _lastChild; }
            set { _lastChild = value; }
        }
 
        internal override bool IsValidChildType(XmlNodeType type)
        {
            return (type == XmlNodeType.Text) || (type == XmlNodeType.EntityReference);
        }
 
        // Gets a value indicating whether the value was explicitly set.
        public virtual bool Specified
        {
            get { return true; }
        }
 
        public override XmlNode? InsertBefore(XmlNode newChild, XmlNode? refChild)
        {
            XmlNode? node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.InsertBefore(newChild, refChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.InsertBefore(newChild, refChild);
            }
 
            return node;
        }
 
        public override XmlNode? InsertAfter(XmlNode newChild, XmlNode? refChild)
        {
            XmlNode? node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.InsertAfter(newChild, refChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.InsertAfter(newChild, refChild);
            }
 
            return node;
        }
 
        public override XmlNode ReplaceChild(XmlNode newChild, XmlNode oldChild)
        {
            XmlNode node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.ReplaceChild(newChild, oldChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.ReplaceChild(newChild, oldChild);
            }
            return node;
        }
 
        public override XmlNode RemoveChild(XmlNode oldChild)
        {
            XmlNode node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.RemoveChild(oldChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.RemoveChild(oldChild);
            }
            return node;
        }
 
        public override XmlNode? PrependChild(XmlNode newChild)
        {
            XmlNode? node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.PrependChild(newChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.PrependChild(newChild);
            }
 
            return node;
        }
 
        public override XmlNode? AppendChild(XmlNode newChild)
        {
            XmlNode? node;
            if (PrepareOwnerElementInElementIdAttrMap())
            {
                string? innerText = InnerText;
                node = base.AppendChild(newChild);
                ResetOwnerElementInElementIdAttrMap(innerText);
            }
            else
            {
                node = base.AppendChild(newChild);
            }
 
            return node;
        }
 
        // DOM Level 2
 
        // Gets the XmlElement node that contains this attribute.
        public virtual XmlElement? OwnerElement
        {
            get
            {
                return parentNode as XmlElement;
            }
        }
 
        // Gets or sets the markup representing just the children of this node.
        public override string InnerXml
        {
            set
            {
                RemoveAll();
                XmlLoader loader = new XmlLoader();
                loader.LoadInnerXmlAttribute(this, value);
            }
        }
 
        // Saves the node to the specified XmlWriter.
        public override void WriteTo(XmlWriter w)
        {
            w.WriteStartAttribute(Prefix, LocalName, NamespaceURI);
            WriteContentTo(w);
            w.WriteEndAttribute();
        }
 
        // Saves all the children of the node to the specified XmlWriter.
        public override void WriteContentTo(XmlWriter w)
        {
            for (XmlNode? node = FirstChild; node != null; node = node.NextSibling)
            {
                node.WriteTo(w);
            }
        }
 
        public override string BaseURI
        {
            get
            {
                if (OwnerElement != null)
                    return OwnerElement.BaseURI;
                return string.Empty;
            }
        }
 
        internal override void SetParent(XmlNode? node)
        {
            this.parentNode = node;
        }
 
        internal override XmlSpace XmlSpace
        {
            get
            {
                if (OwnerElement != null)
                    return OwnerElement.XmlSpace;
                return XmlSpace.None;
            }
        }
 
        internal override string XmlLang
        {
            get
            {
                if (OwnerElement != null)
                    return OwnerElement.XmlLang;
                return string.Empty;
            }
        }
        internal override XPathNodeType XPNodeType
        {
            get
            {
                if (IsNamespace)
                {
                    return XPathNodeType.Namespace;
                }
                return XPathNodeType.Attribute;
            }
        }
 
        internal override string XPLocalName
        {
            get
            {
                if (_name.Prefix.Length == 0 && _name.LocalName == "xmlns") return string.Empty;
                return _name.LocalName;
            }
        }
 
        internal bool IsNamespace
        {
            get
            {
                return Ref.Equal(_name.NamespaceURI, _name.OwnerDocument.strReservedXmlns);
            }
        }
    }
}