2008-02-11 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XElement.cs
1 //
2 // Authors:
3 //   Atsushi Enomoto
4 //
5 // Copyright 2007 Novell (http://www.novell.com)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 using System;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Text;
32 using System.Xml;
33 using System.Xml.Schema;
34 using System.Xml.Serialization;
35
36 namespace System.Xml.Linq
37 {
38         [XmlSchemaProvider (null, IsAny = true)]
39         public class XElement : XContainer, IXmlSerializable
40         {
41                 static IEnumerable <XElement> emptySequence =
42                         new List <XElement> ();
43
44                 public static IEnumerable <XElement> EmptySequence {
45                         get { return emptySequence; }
46                 }
47
48                 XName name;
49                 XAttribute attr_first, attr_last;
50                 bool explicit_is_empty = true;
51
52                 public XElement (XName name, object value)
53                 {
54                         this.name = name;
55                         Add (value);
56                 }
57
58                 public XElement (XElement source)
59                 {
60                         name = source.name;
61                         Add (source.Nodes ());
62                 }
63
64                 public XElement (XName name)
65                 {
66                         this.name = name;
67                 }
68
69                 public XElement (XName name, params object [] contents)
70                 {
71                         this.name = name;
72                         Add (contents);
73                 }
74
75                 public XElement (XStreamingElement source)
76                 {
77                         this.name = source.Name;
78                         Add (source.Contents);
79                 }
80
81                 public static explicit operator bool (XElement element)
82                 {
83                         if (element == null)
84                                 throw new ArgumentNullException ("element");
85                         return XmlConvert.ToBoolean (element.Value);
86                 }
87
88                 public static explicit operator bool? (XElement element)
89                 {
90                         if (element == null)
91                                 return null;
92                         
93                         return element.Value == null ? (bool?) null : XmlConvert.ToBoolean (element.Value);
94                 }
95
96                 public static explicit operator DateTime (XElement element)
97                 {
98                         if (element == null)
99                                 throw new ArgumentNullException ("element");
100                         return XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
101                 }
102
103                 public static explicit operator DateTime? (XElement element)
104                 {
105                         if (element == null)
106                                 return null;
107                         
108                         return element.Value == null ? (DateTime?) null : XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
109                 }
110
111                 public static explicit operator decimal (XElement element)
112                 {
113                         if (element == null)
114                                 throw new ArgumentNullException ("element");
115                         return XmlConvert.ToDecimal (element.Value);
116                 }
117
118                 public static explicit operator decimal? (XElement element)
119                 {
120                         if (element == null)
121                                 return null;
122                         
123                         return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
124                 }
125
126                 public static explicit operator double (XElement element)
127                 {
128                         if (element == null)
129                                 throw new ArgumentNullException ("element");
130                         return XmlConvert.ToDouble (element.Value);
131                 }
132
133                 public static explicit operator double? (XElement element)
134                 {
135                         if (element == null)
136                                 return null;
137                         
138                         return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
139                 }
140
141                 public static explicit operator float (XElement element)
142                 {
143                         if (element == null)
144                                 throw new ArgumentNullException ("element");
145                         return XmlConvert.ToSingle (element.Value);
146                 }
147
148                 public static explicit operator float? (XElement element)
149                 {
150                         if (element == null)
151                                 return null;
152                         
153                         return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
154                 }
155
156                 public static explicit operator Guid (XElement element)
157                 {
158                         if (element == null)
159                                 throw new ArgumentNullException ("element");
160                         return XmlConvert.ToGuid (element.Value);
161                 }
162
163                 public static explicit operator Guid? (XElement element)
164                 {
165                         if (element == null)
166                                 return null;
167                         
168                         return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
169                 }
170
171                 public static explicit operator int (XElement element)
172                 {
173                         if (element == null)
174                                 throw new ArgumentNullException ("element");
175                         return XmlConvert.ToInt32 (element.Value);
176                 }
177
178                 public static explicit operator int? (XElement element)
179                 {
180                         if (element == null)
181                                 return null;
182                         
183                         return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
184                 }
185
186                 public static explicit operator long (XElement element)
187                 {
188                         if (element == null)
189                                 throw new ArgumentNullException ("element");
190                         return XmlConvert.ToInt64 (element.Value);
191                 }
192
193                 public static explicit operator long? (XElement element)
194                 {
195                         if (element == null)
196                                 return null;
197                         
198                         return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
199                 }
200
201                 [CLSCompliant (false)]
202                 public static explicit operator uint (XElement element)
203                 {
204                         if (element == null)
205                                 throw new ArgumentNullException ("element");
206                         return XmlConvert.ToUInt32 (element.Value);
207                 }
208
209                 [CLSCompliant (false)]
210                 public static explicit operator uint? (XElement element)
211                 {
212                         if (element == null)
213                                 return null;
214                         
215                         return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
216                 }
217
218                 [CLSCompliant (false)]
219                 public static explicit operator ulong (XElement element)
220                 {
221                         if (element == null)
222                                 throw new ArgumentNullException ("element");
223                         return XmlConvert.ToUInt64 (element.Value);
224                 }
225
226                 [CLSCompliant (false)]
227                 public static explicit operator ulong? (XElement element)
228                 {
229                         if (element == null)
230                                 return null;
231                         
232                         return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
233                 }
234
235                 public static explicit operator TimeSpan (XElement element)
236                 {
237                         if (element == null)
238                                 throw new ArgumentNullException ("element");
239                         return XmlConvert.ToTimeSpan (element.Value);
240                 }
241
242                 public static explicit operator TimeSpan? (XElement element)
243                 {
244                         if (element == null)
245                                 return null;
246                         
247                         return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
248                 }
249
250                 public static explicit operator string (XElement element)
251                 {
252                         if (element == null)
253                                 return null;
254                         
255                         return element.Value;
256                 }
257
258                 public XAttribute FirstAttribute {
259                         get { return attr_first; }
260                         internal set { attr_first = value; }
261                 }
262
263                 public XAttribute LastAttribute {
264                         get { return attr_last; }
265                         internal set { attr_last = value; }
266                 }
267
268                 public bool HasAttributes {
269                         get { return attr_first != null; }
270                 }
271
272                 public bool HasElements {
273                         get {
274                                 foreach (object o in Nodes ())
275                                         if (o is XElement)
276                                                 return true;
277                                 return false;
278                         }
279                 }
280
281                 public bool IsEmpty {
282                         get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
283                         internal set { explicit_is_empty = value; }
284                 }
285
286                 public XName Name {
287                         get { return name; }
288                         set {
289                                 if (name == null)
290                                         throw new ArgumentNullException ("value");
291                                 name = value;
292                         }
293                 }
294
295                 public override XmlNodeType NodeType {
296                         get { return XmlNodeType.Element; }
297                 }
298
299                 public string Value {
300                         get {
301                                 StringBuilder sb = null;
302                                 foreach (object s in Nodes ()) {
303                                         if (sb == null)
304                                                 sb = new StringBuilder ();
305                                         sb.Append (s);
306                                 }
307                                 return sb == null ? String.Empty : sb.ToString ();
308                         }
309                         set {
310                                 RemoveNodes ();
311                                 Add (value);
312                         }
313                 }
314
315                 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
316                 {
317                         List <XElement> list = new List <XElement> ();
318                         if (getMeIn)
319                                 list.Add (this);
320                         for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
321                                 if (name == null || el.Name == name)
322                                         list.Add (el);
323                         return list;
324                 }
325
326                 public XAttribute Attribute (XName name)
327                 {
328                         foreach (XAttribute a in Attributes ())
329                                 if (a.Name == name)
330                                         return a;
331                         return null;
332                 }
333
334                 public IEnumerable <XAttribute> Attributes ()
335                 {
336                         XAttribute next;
337                         for (XAttribute a = attr_first; a != null; a = next) {
338                                 next = a.NextAttribute;
339                                 yield return a;
340                         }
341                 }
342
343                 // huh?
344                 public IEnumerable <XAttribute> Attributes (XName name)
345                 {
346                         foreach (XAttribute a in Attributes ())
347                                 if (a.Name == name)
348                                         yield return a;
349                 }
350
351                 public static XElement Load (string uri)
352                 {
353                         return Load (uri, LoadOptions.None);
354                 }
355
356                 public static XElement Load (string uri, LoadOptions options)
357                 {
358                         XmlReaderSettings s = new XmlReaderSettings ();
359                         s.ProhibitDtd = false;
360                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
361                         using (XmlReader r = XmlReader.Create (uri, s)) {
362                                 return LoadCore (r, options);
363                         }
364                 }
365
366                 public static XElement Load (TextReader tr)
367                 {
368                         return Load (tr, LoadOptions.None);
369                 }
370
371                 public static XElement Load (TextReader tr, LoadOptions options)
372                 {
373                         XmlReaderSettings s = new XmlReaderSettings ();
374                         s.ProhibitDtd = false;
375                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
376                         using (XmlReader r = XmlReader.Create (tr, s)) {
377                                 return LoadCore (r, options);
378                         }
379                 }
380
381                 public static XElement Load (XmlReader reader)
382                 {
383                         return Load (reader, LoadOptions.None);
384                 }
385
386                 public static XElement Load (XmlReader reader, LoadOptions options)
387                 {
388                         XmlReaderSettings s = reader.Settings.Clone ();
389                         s.ProhibitDtd = false;
390                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
391                         using (XmlReader r = XmlReader.Create (reader, s)) {
392                                 return LoadCore (r, options);
393                         }
394                 }
395
396                 static XElement LoadCore (XmlReader r, LoadOptions options)
397                 {
398                         r.MoveToContent ();
399                         if (r.NodeType != XmlNodeType.Element)
400                                 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
401                         XName name = XName.Get (r.LocalName, r.NamespaceURI);
402                         XElement e = new XElement (name);
403                         e.FillLineInfoAndBaseUri (r, options);
404
405                         if (r.MoveToFirstAttribute ()) {
406                                 do {
407                                         // not sure how current Orcas behavior makes sense here though ...
408                                         if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
409                                                 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
410                                         else
411                                                 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
412                                         e.LastAttribute.FillLineInfoAndBaseUri (r, options);
413                                 } while (r.MoveToNextAttribute ());
414                                 r.MoveToElement ();
415                         }
416                         if (!r.IsEmptyElement) {
417                                 r.Read ();
418                                 e.ReadContentFrom (r, options);
419                                 r.ReadEndElement ();
420                                 e.explicit_is_empty = false;
421                         } else {
422                                 e.explicit_is_empty = true;
423                                 r.Read ();
424                         }
425                         return e;
426                 }
427
428                 public static XElement Parse (string s)
429                 {
430                         return Parse (s, LoadOptions.None);
431                 }
432
433                 public static XElement Parse (string s, LoadOptions options)
434                 {
435                         return Load (new StringReader (s), options);
436                 }
437
438                 public void RemoveAll ()
439                 {
440                         RemoveAttributes ();
441                         RemoveNodes ();
442                 }
443
444                 public void RemoveAttributes ()
445                 {
446                         while (attr_first != null)
447                                 attr_last.Remove ();
448                 }
449
450                 public void Save (string filename)
451                 {
452                         Save (filename, SaveOptions.None);
453                 }
454
455                 public void Save (string filename, SaveOptions options)
456                 {
457                         XmlWriterSettings s = new XmlWriterSettings ();
458                         s.Indent = options != SaveOptions.DisableFormatting;
459                         using (XmlWriter w = XmlWriter.Create (filename, s)) {
460                                 Save (w);
461                         }
462                 }
463
464                 public void Save (TextWriter tw)
465                 {
466                         Save (tw, SaveOptions.None);
467                 }
468
469                 public void Save (TextWriter tw, SaveOptions options)
470                 {
471                         XmlWriterSettings s = new XmlWriterSettings ();
472                         s.Indent = options != SaveOptions.DisableFormatting;
473                         using (XmlWriter w = XmlWriter.Create (tw, s)) {
474                                 Save (w);
475                         }
476                 }
477
478                 public void Save (XmlWriter w)
479                 {
480                         WriteTo (w);
481                 }
482
483                 public IEnumerable <XElement> AncestorsAndSelf ()
484                 {
485                         return GetAncestorList (null, true);
486                 }
487
488                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
489                 {
490                         return GetAncestorList (name, true);
491                 }
492
493                 public IEnumerable <XElement> DescendantsAndSelf ()
494                 {
495                         List <XElement> list = new List <XElement> ();
496                         list.Add (this);
497                         list.AddRange (Descendants ());
498                         return list;
499                 }
500
501                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
502                 {
503                         List <XElement> list = new List <XElement> ();
504                         if (name == this.name)
505                                 list.Add (this);
506                         list.AddRange (Descendants (name));
507                         return list;
508                 }
509
510                 public IEnumerable <XNode> DescendantNodesAndSelf ()
511                 {
512                         yield return this;
513                         foreach (XNode node in DescendantNodes ())
514                                 yield return node;
515                 }
516
517                 public void SetAttributeValue (XName name, object value)
518                 {
519                         XAttribute a = Attribute (name);
520                         if (value == null) {
521                                 if (a != null)
522                                         a.Remove ();
523                         } else {
524                                 if (a == null) {
525                                         a = new XAttribute (name, value);
526                                         a.SetOwner (this);
527                                         if (attr_first == null) {
528                                                 attr_first = a;
529                                                 attr_last = a;
530                                         } else {
531                                                 attr_last.NextAttribute = a;
532                                                 a.PreviousAttribute = attr_last;
533                                                 attr_last = a;
534                                         }
535                                 }
536                                 else
537                                         a.Value = XUtil.ToString (value);
538                         }
539                 }
540
541                 public override void WriteTo (XmlWriter w)
542                 {
543                         w.WriteStartElement (name.LocalName, name.Namespace.NamespaceName);
544
545                         foreach (XAttribute a in Attributes ()) {
546                                 if (a.IsNamespaceDeclaration) {
547                                         if (a.Name.Namespace == XNamespace.Xmlns)
548                                                 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
549                                         else
550                                                 w.WriteAttributeString ("xmlns", a.Value);
551                                 }
552                                 else
553                                         w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
554                         }
555
556                         foreach (XNode node in Nodes ())
557                                 node.WriteTo (w);
558
559                         if (explicit_is_empty)
560                                 w.WriteEndElement ();
561                         else
562                                 w.WriteFullEndElement ();
563                 }
564
565                 public XNamespace GetDefaultNamespace ()
566                 {
567                         for (XElement el = this; el != null; el = el.Parent)
568                                 foreach (XAttribute a in el.Attributes ())
569                                         if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
570                                                 return XNamespace.Get (a.Value);
571                         return XNamespace.None; // nothing is declared.
572                 }
573
574                 public XNamespace GetNamespaceOfPrefix (string prefix)
575                 {
576                         for (XElement el = this; el != null; el = el.Parent)
577                                 foreach (XAttribute a in el.Attributes ())
578                                         if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix)
579                                                 return XNamespace.Get (a.Value);
580                         return XNamespace.None; // nothing is declared.
581                 }
582
583                 public string GetPrefixOfNamespace (XNamespace ns)
584                 {
585                         foreach (string prefix in GetPrefixOfNamespaceCore (ns))
586                                 if (GetNamespaceOfPrefix (prefix) == ns)
587                                         return prefix;
588                         return null; // nothing is declared
589                 }
590                 
591                 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
592                 {
593                         for (XElement el = this; el != null; el = el.Parent)
594                                 foreach (XAttribute a in el.Attributes ())
595                                         if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
596                                                 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
597                 }
598
599                 public void ReplaceAll (object item)
600                 {
601                         RemoveNodes ();
602                         Add (item);
603                 }
604
605                 public void ReplaceAll (params object [] items)
606                 {
607                         RemoveNodes ();
608                         Add (items);
609                 }
610
611                 public void ReplaceAttributes (object item)
612                 {
613                         RemoveAttributes ();
614                         Add (item);
615                 }
616
617                 public void ReplaceAttributes (params object [] items)
618                 {
619                         RemoveAttributes ();
620                         Add (items);
621                 }
622
623                 public void SetElementValue (XName name, object value)
624                 {
625                         XElement el = new XElement (name, value);
626                         RemoveNodes ();
627                         Add (el);
628                 }
629
630                 public void SetValue (object value)
631                 {
632                         RemoveNodes ();
633                         foreach (XNode n in XUtil.ToNodes (value))
634                                 Add (n);
635                 }
636
637                 internal override void OnAdded (XNode node, bool addFirst)
638                 {
639                         if (node is XDocument || node is XDocumentType)
640                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", node.GetType ()));
641                 }
642
643                 void IXmlSerializable.WriteXml (XmlWriter writer)
644                 {
645                         Save (writer);
646                 }
647
648                 void IXmlSerializable.ReadXml (XmlReader reader)
649                 {
650                         ReadContentFrom (reader, LoadOptions.None);
651                 }
652
653                 XmlSchema IXmlSerializable.GetSchema ()
654                 {
655                         return null;
656                 }
657         }
658 }