2005-12-05 Lluis Sanchez Gual <lluis@novell.com>
[mono.git] / mcs / class / System.XML / System.Xml / XmlAttribute.cs
1 //
2 // System.Xml.XmlAttribute
3 //
4 // Authors:
5 //   Jason Diamond (jason@injektilo.org)
6 //   Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //
8 // (C) 2002 Jason Diamond  http://injektilo.org/
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Text;
35 using System.Xml.XPath;
36 using Mono.Xml;
37 #if NET_2_0
38 using System.Xml.Schema;
39 #endif
40
41 namespace System.Xml
42 {
43         public class XmlAttribute : XmlNode
44         {
45                 #region Fields
46
47                 private XmlNameEntry name;
48                 internal bool isDefault;
49 #if NET_2_0
50                 private IXmlSchemaInfo schemaInfo;
51 #endif
52
53                 #endregion
54
55                 #region Constructor
56
57                 protected internal XmlAttribute (
58                         string prefix, 
59                         string localName, 
60                         string namespaceURI,
61                         XmlDocument doc) : this (prefix, localName, namespaceURI, doc, false, true)
62                 {
63                 }
64
65                 internal XmlAttribute (
66                         string prefix, 
67                         string localName, 
68                         string namespaceURI,
69                         XmlDocument doc,
70                         bool atomizedNames, bool checkNamespace) : base (doc)
71                 {
72                         if (prefix == null)
73                                 prefix = String.Empty;
74                         if (namespaceURI == null)
75                                 namespaceURI = String.Empty;
76
77                         // Prefix "xml" should be also checked (http://www.w3.org/XML/xml-names-19990114-errata#NE05)
78                         // but MS.NET ignores such case
79                         if (checkNamespace) {
80                                 if (prefix == "xmlns" || (prefix == "" && localName == "xmlns"))
81                                         if (namespaceURI != XmlNamespaceManager.XmlnsXmlns)
82                                                 throw new ArgumentException ("Invalid attribute namespace for namespace declaration.");
83                                 else if (prefix == "xml" && namespaceURI != XmlNamespaceManager.XmlnsXml)
84                                         throw new ArgumentException ("Invalid attribute namespace for namespace declaration.");
85                         }
86
87                         // There are no means to identify the DOM is namespace-
88                         // aware or not, so we can only check Name validity.
89                         if (prefix != "" && !XmlChar.IsName (prefix))
90                                 throw new ArgumentException ("Invalid attribute prefix.");
91                         else if (!XmlChar.IsName (localName))
92                                 throw new ArgumentException ("Invalid attribute local name.");
93
94                         if (!atomizedNames) {
95                                 prefix = doc.NameTable.Add (prefix);
96                                 localName = doc.NameTable.Add (localName);
97                                 namespaceURI = doc.NameTable.Add (namespaceURI);
98                         }
99                         name = doc.NameCache.Add (prefix, localName, namespaceURI, true);
100                 }
101
102                 #endregion
103
104                 #region Properties
105
106                 public override string BaseURI {
107                         get { return OwnerElement != null ? OwnerElement.BaseURI : String.Empty; }
108                 }
109
110                 public override string InnerText {
111 #if !(NET_2_0)
112                         get {
113                                 return base.InnerText;
114                         }
115 #endif
116
117                         set {
118                                 Value = value;
119                         }
120                 }
121
122                 public override string InnerXml {
123 #if !(NET_2_0)
124                         get {
125                                 // Not sure why this is an override.  Passing through for now.
126                                 return base.InnerXml;
127                         }
128 #endif
129
130                         set {
131                                 RemoveAll ();
132                                 XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
133                                 XmlParserContext ctx = new XmlParserContext (OwnerDocument.NameTable, nsmgr,
134                                         OwnerDocument.DocumentType != null ? OwnerDocument.DocumentType.DTD : null,
135                                         BaseURI, XmlLang, XmlSpace, null);
136                                 XmlTextReader xtr = new XmlTextReader (value, XmlNodeType.Attribute, ctx);
137                                 xtr.XmlResolver = OwnerDocument.Resolver;
138                                 xtr.Read ();
139                                 OwnerDocument.ReadAttributeNodeValue (xtr, this);
140                         }
141                 }
142
143                 public override string LocalName {
144                         get {
145                                 return name.LocalName;
146                         }
147                 }
148
149                 public override string Name {
150                         get { 
151                                 return name.Prefix != String.Empty ? OwnerDocument.NameTable.Add (name.Prefix + ":" + name.LocalName) : name.LocalName;
152                         }
153                 }
154
155                 public override string NamespaceURI {
156                         get {
157                                 return name.NS;
158                         }
159                 }
160
161                 public override XmlNodeType NodeType {
162                         get {
163                                 return XmlNodeType.Attribute;
164                         }
165                 }
166
167                 internal override XPathNodeType XPathNodeType {
168                         get {
169                                 return XPathNodeType.Attribute;
170                         }
171                 }
172
173                 public override XmlDocument OwnerDocument {
174                         get {
175                                 return base.OwnerDocument;
176                         }
177                 }
178
179                 public virtual XmlElement OwnerElement {
180                         get { return AttributeOwnerElement; }
181                 }
182
183                 public override XmlNode ParentNode {
184                         get {
185                                 // It always returns null (by specification).
186                                 return null;
187                         }
188                 }
189
190                 // We gotta do more in the set block here
191                 // We need to do the proper tests and throw
192                 // the correct Exceptions
193                 //
194                 // Wrong cases are: (1)check readonly, (2)check character validity,
195                 // (3)check format validity, (4)this is attribute and qualifiedName != "xmlns"
196                 public override string Prefix {
197                         set {
198                                 if (IsReadOnly)
199                                         throw new XmlException ("This node is readonly.");
200                                 if (name.Prefix == "xmlns" && value != "xmlns")
201                                         throw new ArgumentException ("Cannot bind to the reserved namespace.");
202
203                                 value = OwnerDocument.NameTable.Add (value);
204                                 name = OwnerDocument.NameCache.Add (value,
205                                         name.LocalName, name.NS, true);
206                         }
207                         
208                         get {
209                                 return name.Prefix;
210                         }
211                 }
212
213 #if NET_2_0
214                 public override IXmlSchemaInfo SchemaInfo {
215                         get { return schemaInfo; }
216                         internal set { schemaInfo = value; }
217                 }
218 #endif
219
220                 public virtual bool Specified {
221                         get {
222                                 return !isDefault;
223                         }
224                 }
225
226                 public override string Value {
227                         get { return InnerText; }
228
229                         set {
230                                 if (this.IsReadOnly)
231                                         throw new ArgumentException ("Attempt to modify a read-only node.");
232                                 OwnerDocument.CheckIdTableUpdate (this, InnerText, value);
233
234                                 XmlNode textChild = FirstChild as XmlCharacterData;
235                                 if (textChild == null) {
236                                         this.RemoveAll ();
237                                         AppendChild (OwnerDocument.CreateTextNode (value));
238                                 }
239                                 else if (FirstChild.NextSibling != null) {
240                                         this.RemoveAll ();
241                                         AppendChild (OwnerDocument.CreateTextNode (value));
242                                 }
243                                 else
244                                         textChild.Value = value;
245                                 isDefault = false;
246                         }
247                 }
248
249                 internal override string XmlLang {
250                         get { return OwnerElement != null ? OwnerElement.XmlLang : String.Empty; }
251                 }
252
253                 internal override XmlSpace XmlSpace {
254                         get { return OwnerElement != null ? OwnerElement.XmlSpace : XmlSpace.None; }
255                 }
256
257                 #endregion
258
259                 #region Methods
260
261 #if NET_2_0
262                 public override XmlNode AppendChild (XmlNode child)
263                 {
264                         return base.AppendChild (child);
265                 }
266
267                 public override XmlNode InsertBefore (XmlNode newChild, XmlNode refChild)
268                 {
269                         return base.InsertBefore (newChild, refChild);
270                 }
271
272                 public override XmlNode InsertAfter (XmlNode newChild, XmlNode refChild)
273                 {
274                         return base.InsertAfter (newChild, refChild);
275                 }
276
277                 public override XmlNode PrependChild (XmlNode node)
278                 {
279                         return base.PrependChild (node);
280                 }
281
282                 public override XmlNode RemoveChild (XmlNode node)
283                 {
284                         return base.RemoveChild (node);
285                 }
286
287                 public override XmlNode ReplaceChild (XmlNode newChild, XmlNode oldChild)
288                 {
289                         return base.ReplaceChild (newChild, oldChild);
290                 }
291 #endif
292
293                 public override XmlNode CloneNode (bool deep)
294                 {
295                         XmlNode node = new XmlAttribute (name.Prefix, name.LocalName, name.NS,
296                                                          OwnerDocument, true, false);
297                         if (deep) {
298                                 for (int i = 0; i < ChildNodes.Count; i++)
299                                         node.AppendChild (ChildNodes [i].CloneNode (deep));
300                         }
301
302                         return node;
303                 }
304
305                 internal void SetDefault ()
306                 {
307                         isDefault = true;
308                 }
309
310                 public override void WriteContentTo (XmlWriter w)
311                 {
312                         for (int i = 0; i < ChildNodes.Count; i++)
313                                 ChildNodes [i].WriteTo (w);
314                 }
315
316                 public override void WriteTo (XmlWriter w)
317                 {
318                         if (isDefault)
319                                 return; // Write nothing.
320                         w.WriteStartAttribute (name.Prefix, name.LocalName, name.NS);
321                         WriteContentTo (w);
322                         w.WriteEndAttribute ();
323                 }
324
325                 internal DTDAttributeDefinition GetAttributeDefinition ()
326                 {
327                         if (OwnerElement == null)
328                                 return null;
329
330                         // If it is default, then directly create new attribute.
331                         DTDAttListDeclaration attList = OwnerDocument.DocumentType != null ? OwnerDocument.DocumentType.DTD.AttListDecls [OwnerElement.Name] : null;
332                         return attList != null ? attList [Name] : null;
333                 }
334                 #endregion
335         }
336 }