e72968196193ffb99bc0081b5074e3cc2464bd56
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Dom / XmlAttribute.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlAttribute.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml {
9     using System;
10     using System.Xml.Schema;
11     using System.Xml.XPath;
12     using System.Diagnostics;
13
14     // Represents an attribute of the XMLElement object. Valid and default
15     // values for the attribute are defined in a DTD or schema.
16     public class XmlAttribute : XmlNode {
17         XmlName name;
18         XmlLinkedNode lastChild;
19         
20         internal XmlAttribute( XmlName name, XmlDocument doc ): base( doc ) {
21             Debug.Assert(name!=null);
22             Debug.Assert(doc!=null);
23             this.parentNode = null;
24             if ( !doc.IsLoading ) {
25                 XmlDocument.CheckName( name.Prefix );
26                 XmlDocument.CheckName( name.LocalName );
27             }
28             if (name.LocalName.Length == 0)
29                 throw new ArgumentException(Res.GetString(Res.Xdom_Attr_Name));
30             this.name = name;
31         }
32
33         internal int LocalNameHash {
34             get { return name.HashCode; }
35         }
36
37         protected internal XmlAttribute( string prefix, string localName, string namespaceURI, XmlDocument doc )
38         : this(doc.AddAttrXmlName(prefix, localName, namespaceURI, null), doc) {
39         }
40
41         internal XmlName XmlName {
42             get { return name; }
43             set { name = value; }
44         }
45
46         // Creates a duplicate of this node.
47         public override XmlNode CloneNode(bool deep) {
48             // CloneNode for attributes is deep irrespective of parameter 'deep' value     
49             Debug.Assert( OwnerDocument != null );
50             XmlDocument doc = OwnerDocument;
51             XmlAttribute attr = doc.CreateAttribute( Prefix, LocalName, NamespaceURI );
52             attr.CopyChildren( doc, this, true );
53             return attr;
54         }
55
56         // Gets the parent of this node (for nodes that can have parents).
57         public override XmlNode ParentNode {
58             get { return null;}
59         }
60
61         // Gets the name of the node.
62         public override String Name { 
63             get { return name.Name;}
64         }
65
66         // Gets the name of the node without the namespace prefix.
67         public override String LocalName { 
68             get { return name.LocalName;}
69         }
70
71         // Gets the namespace URI of this node.
72         public override String NamespaceURI { 
73             get { return name.NamespaceURI;} 
74         }
75
76         // Gets or sets the namespace prefix of this node.
77         public override String Prefix { 
78             get { return name.Prefix;}
79             set { name = name.OwnerDocument.AddAttrXmlName( value, LocalName, NamespaceURI, SchemaInfo ); }
80         }
81
82         // Gets the type of the current node.
83         public override XmlNodeType NodeType {
84             get { return XmlNodeType.Attribute;}
85         }
86
87         // Gets the XmlDocument that contains this node.
88         public override XmlDocument OwnerDocument { 
89             get { 
90                 return name.OwnerDocument;
91             }
92         }
93
94         // Gets or sets the value of the node.
95         public override String Value { 
96             get { return InnerText; }
97             set { InnerText = value; } //use InnerText which has perf optimization
98         }
99
100         public override IXmlSchemaInfo SchemaInfo {
101             get {
102                 return name;
103             }
104         }
105
106         public override String InnerText {
107             set {
108                 if (PrepareOwnerElementInElementIdAttrMap()) {
109                     string innerText = base.InnerText;
110                     base.InnerText = value;
111                     ResetOwnerElementInElementIdAttrMap(innerText);
112                 }
113                 else {
114                     base.InnerText = value;
115                 }
116             }
117         }
118
119         internal bool PrepareOwnerElementInElementIdAttrMap() {
120             XmlDocument ownerDocument = OwnerDocument; 
121             if (ownerDocument.DtdSchemaInfo != null) { // DTD exists
122                 XmlElement ownerElement = OwnerElement;
123                 if (ownerElement != null) {
124                     return ownerElement.Attributes.PrepareParentInElementIdAttrMap(Prefix, LocalName);
125                 }
126             }
127             return false;
128         }
129
130         internal void ResetOwnerElementInElementIdAttrMap(string oldInnerText) {
131             XmlElement ownerElement = OwnerElement;
132             if (ownerElement != null) {
133                 ownerElement.Attributes.ResetParentInElementIdAttrMap(oldInnerText, InnerText);
134             }
135         }
136
137         internal override bool IsContainer {
138             get { return true;}
139         }
140
141         //the function is provided only at Load time to speed up Load process
142         internal override XmlNode AppendChildForLoad(XmlNode newChild, XmlDocument doc) {
143             XmlNodeChangedEventArgs args = doc.GetInsertEventArgsForLoad( newChild, this );
144
145             if (args != null)
146                 doc.BeforeEvent( args );
147
148             XmlLinkedNode newNode = (XmlLinkedNode)newChild;
149
150             if (lastChild == null) { // if LastNode == null
151                 newNode.next = newNode;
152                 lastChild = newNode;
153                 newNode.SetParentForLoad(this);
154             }
155             else {
156                 XmlLinkedNode refNode = lastChild; // refNode = LastNode;
157                 newNode.next = refNode.next;
158                 refNode.next = newNode;
159                 lastChild = newNode; // LastNode = newNode;
160                 if (refNode.IsText
161                     && newNode.IsText) {
162                     NestTextNodes(refNode, newNode);
163                 }
164                 else {
165                     newNode.SetParentForLoad(this);
166                 }
167             }
168
169             if (args != null)
170                 doc.AfterEvent( args );
171
172             return newNode;
173         }
174
175         internal override XmlLinkedNode LastNode {
176             get { return lastChild;}
177             set { lastChild = value;}
178         }
179         
180         internal override bool IsValidChildType( XmlNodeType type ) {
181             return(type == XmlNodeType.Text) || (type == XmlNodeType.EntityReference);
182         }
183
184         // Gets a value indicating whether the value was explicitly set.
185         public virtual bool Specified { 
186             get { return true;}
187         }
188
189         public override XmlNode InsertBefore(XmlNode newChild, XmlNode refChild) {
190             XmlNode node;
191             if (PrepareOwnerElementInElementIdAttrMap()) {
192                 string innerText = InnerText;
193                 node = base.InsertBefore(newChild, refChild);
194                 ResetOwnerElementInElementIdAttrMap(innerText);
195             }
196             else {
197                 node = base.InsertBefore(newChild, refChild);
198             }
199             return node;
200         }
201
202         public override XmlNode InsertAfter(XmlNode newChild, XmlNode refChild) {
203             XmlNode node;
204             if (PrepareOwnerElementInElementIdAttrMap()) {
205                 string innerText = InnerText;
206                 node = base.InsertAfter(newChild, refChild);
207                 ResetOwnerElementInElementIdAttrMap(innerText);
208             }
209             else {
210                 node = base.InsertAfter(newChild, refChild);
211             }
212             return node;
213         }
214
215         public override XmlNode ReplaceChild(XmlNode newChild, XmlNode oldChild) {
216             XmlNode node;
217             if (PrepareOwnerElementInElementIdAttrMap()) {
218                 string innerText = InnerText;
219                 node = base.ReplaceChild(newChild, oldChild);
220                 ResetOwnerElementInElementIdAttrMap(innerText);
221             }
222             else {
223                 node = base.ReplaceChild(newChild, oldChild);
224             }
225             return node;
226         }
227
228         public override XmlNode RemoveChild(XmlNode oldChild) {
229             XmlNode node;
230             if (PrepareOwnerElementInElementIdAttrMap()) {
231                 string innerText = InnerText;
232                 node = base.RemoveChild(oldChild);
233                 ResetOwnerElementInElementIdAttrMap(innerText);
234             }
235             else {
236                 node = base.RemoveChild(oldChild);
237             }
238             return node;
239         }
240
241         public override XmlNode PrependChild(XmlNode newChild) {
242             XmlNode node;
243             if (PrepareOwnerElementInElementIdAttrMap()) {
244                 string innerText = InnerText;
245                 node = base.PrependChild(newChild);
246                 ResetOwnerElementInElementIdAttrMap(innerText);
247             }
248             else {
249                 node = base.PrependChild(newChild);
250             }
251             return node;
252         }
253
254         public override XmlNode AppendChild(XmlNode newChild) {
255             XmlNode node;
256             if (PrepareOwnerElementInElementIdAttrMap()) {
257                 string innerText = InnerText;
258                 node = base.AppendChild(newChild);
259                 ResetOwnerElementInElementIdAttrMap(innerText);
260             }
261             else {
262                 node = base.AppendChild(newChild);
263             }
264             return node;
265         }
266
267         // DOM Level 2
268
269         // Gets the XmlElement node that contains this attribute.
270         public virtual XmlElement OwnerElement { 
271             get { 
272                 return parentNode as XmlElement;
273             }
274         }
275
276         // Gets or sets the markup representing just the children of this node.
277         public override string InnerXml {
278             set {
279                 RemoveAll();
280                 XmlLoader loader = new XmlLoader();
281                 loader.LoadInnerXmlAttribute( this, value );
282             }
283         }
284
285         // Saves the node to the specified XmlWriter.
286         public override void WriteTo(XmlWriter w) {
287             w.WriteStartAttribute( Prefix, LocalName, NamespaceURI );
288             WriteContentTo(w);
289             w.WriteEndAttribute();
290         }
291
292         // Saves all the children of the node to the specified XmlWriter.
293         public override void WriteContentTo(XmlWriter w) {
294             for (XmlNode node = FirstChild; node != null; node = node.NextSibling) {
295                 node.WriteTo(w);
296             }
297         }
298
299         public override String BaseURI {
300             get {
301                 if ( OwnerElement != null )
302                     return OwnerElement.BaseURI;
303                 return String.Empty;
304             }
305         }
306
307         internal override void SetParent(XmlNode node) {
308             this.parentNode = node;
309         }
310         
311         internal override XmlSpace XmlSpace {
312             get {
313                 if ( OwnerElement != null )
314                     return OwnerElement.XmlSpace;
315                 return XmlSpace.None;
316             }
317         }
318
319         internal override String XmlLang {
320             get {
321                 if ( OwnerElement != null )
322                     return OwnerElement.XmlLang;
323                 return String.Empty;
324             }
325         }
326         internal override XPathNodeType XPNodeType { 
327             get {
328                 if (IsNamespace) {
329                     return XPathNodeType.Namespace; 
330                 }
331                 return XPathNodeType.Attribute;
332             }
333         }
334
335         internal override string XPLocalName { 
336             get {
337                 if (name.Prefix.Length == 0 && name.LocalName == "xmlns") return string.Empty;
338                 return name.LocalName;
339             }
340         }
341
342         internal bool IsNamespace {
343             get {
344                 return Ref.Equal(name.NamespaceURI, name.OwnerDocument.strReservedXmlns);
345             }
346         }
347     }
348 }