2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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                         get {
112                                 return base.InnerText;
113                         }
114
115                         set {
116                                 Value = value;
117                         }
118                 }
119
120                 public override string InnerXml {
121                         get {
122                                 // Not sure why this is an override.  Passing through for now.
123                                 return base.InnerXml;
124                         }
125
126                         set {
127                                 RemoveAll ();
128                                 XmlNamespaceManager nsmgr = ConstructNamespaceManager ();
129                                 XmlParserContext ctx = new XmlParserContext (OwnerDocument.NameTable, nsmgr,
130                                         OwnerDocument.DocumentType != null ? OwnerDocument.DocumentType.DTD : null,
131                                         BaseURI, XmlLang, XmlSpace, null);
132                                 XmlTextReader xtr = new XmlTextReader (value, XmlNodeType.Attribute, ctx);
133                                 xtr.XmlResolver = OwnerDocument.Resolver;
134                                 xtr.Read ();
135                                 OwnerDocument.ReadAttributeNodeValue (xtr, this);
136                         }
137                 }
138
139                 public override string LocalName {
140                         get {
141                                 return name.LocalName;
142                         }
143                 }
144
145                 public override string Name {
146                         get { 
147                                 return name.Prefix != String.Empty ? OwnerDocument.NameTable.Add (name.Prefix + ":" + name.LocalName) : name.LocalName;
148                         }
149                 }
150
151                 public override string NamespaceURI {
152                         get {
153                                 return name.NS;
154                         }
155                 }
156
157                 public override XmlNodeType NodeType {
158                         get {
159                                 return XmlNodeType.Attribute;
160                         }
161                 }
162
163                 internal override XPathNodeType XPathNodeType {
164                         get {
165                                 return XPathNodeType.Attribute;
166                         }
167                 }
168
169                 public override XmlDocument OwnerDocument {
170                         get {
171                                 return base.OwnerDocument;
172                         }
173                 }
174
175                 public virtual XmlElement OwnerElement {
176                         get { return AttributeOwnerElement; }
177                 }
178
179                 public override XmlNode ParentNode {
180                         get {
181                                 // It always returns null (by specification).
182                                 return null;
183                         }
184                 }
185
186                 // We gotta do more in the set block here
187                 // We need to do the proper tests and throw
188                 // the correct Exceptions
189                 //
190                 // Wrong cases are: (1)check readonly, (2)check character validity,
191                 // (3)check format validity, (4)this is attribute and qualifiedName != "xmlns"
192                 public override string Prefix {
193                         set {
194                                 if (IsReadOnly)
195                                         throw new XmlException ("This node is readonly.");
196                                 if (!XmlChar.IsNCName (value))
197                                         throw new ArgumentException ("Specified name is not a valid NCName: " + value);
198                                 if (name.Prefix == "xmlns" && value != "xmlns")
199                                         throw new ArgumentException ("Cannot bind to the reserved namespace.");
200
201                                 value = OwnerDocument.NameTable.Add (value);
202                                 name = OwnerDocument.NameCache.Add (value,
203                                         name.LocalName, name.NS, true);
204                         }
205                         
206                         get {
207                                 return name.Prefix;
208                         }
209                 }
210
211 #if NET_2_0
212                 [CLSCompliant(false)]
213                 public override IXmlSchemaInfo SchemaInfo {
214                         get { return schemaInfo; }
215                         internal set { schemaInfo = value; }
216                 }
217 #endif
218
219                 public virtual bool Specified {
220                         get {
221                                 return !isDefault;
222                         }
223                 }
224
225                 private string BuildChildValue (XmlNodeList list)
226                 {
227                         string ret = String.Empty;
228                         for (int i = 0; i < list.Count; i++) {
229                                 if (list [i].NodeType == XmlNodeType.EntityReference)
230                                         ret += BuildChildValue (list [i].ChildNodes);
231                                 else
232                                         ret += list [i].Value;
233                         }
234                         return ret;
235                 }
236
237                 public override string Value {
238                         get { return BuildChildValue (ChildNodes); }
239
240                         set {
241                                 if (this.IsReadOnly)
242                                         throw new ArgumentException ("Attempt to modify a read-only node.");
243                                 XmlNode firstChild = FirstChild;
244                                 if (firstChild == null)
245                                         AppendChild (OwnerDocument.CreateTextNode (value));
246                                 else if (FirstChild.NextSibling != null) {
247                                         this.RemoveAll ();
248                                         AppendChild (OwnerDocument.CreateTextNode (value));
249                                 }
250                                 else
251                                         firstChild.Value = value;
252                                 isDefault = false;
253                         }
254                 }
255
256                 internal override string XmlLang {
257                         get { return OwnerElement != null ? OwnerElement.XmlLang : String.Empty; }
258                 }
259
260                 internal override XmlSpace XmlSpace {
261                         get { return OwnerElement != null ? OwnerElement.XmlSpace : XmlSpace.None; }
262                 }
263
264                 #endregion
265
266                 #region Methods
267
268                 public override XmlNode CloneNode (bool deep)
269                 {
270                         XmlNode node = new XmlAttribute (name.Prefix, name.LocalName, name.NS,
271                                                          OwnerDocument, true, false);
272                         if (deep) {
273                                 for (int i = 0; i < ChildNodes.Count; i++)
274                                         node.AppendChild (ChildNodes [i].CloneNode (deep));
275                         }
276
277                         if (IsReadOnly)
278                                 node.SetReadOnly ();
279                         return node;
280                 }
281
282                 internal void SetDefault ()
283                 {
284                         isDefault = true;
285                 }
286
287                 public override void WriteContentTo (XmlWriter w)
288                 {
289                         for (int i = 0; i < ChildNodes.Count; i++)
290                                 ChildNodes [i].WriteTo (w);
291                 }
292
293                 public override void WriteTo (XmlWriter w)
294                 {
295                         w.WriteStartAttribute (name.Prefix, name.LocalName, name.NS);
296                         WriteContentTo (w);
297                         w.WriteEndAttribute ();
298                 }
299
300                 internal DTDAttributeDefinition GetAttributeDefinition ()
301                 {
302                         if (OwnerElement == null)
303                                 return null;
304
305                         // If it is default, then directly create new attribute.
306                         DTDAttListDeclaration attList = OwnerDocument.DocumentType != null ? OwnerDocument.DocumentType.DTD.AttListDecls [OwnerElement.Name] : null;
307                         return attList != null ? attList [Name] : null;
308                 }
309                 #endregion
310         }
311 }