2010-01-09 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.Attributes ());
62                         Add (source.Nodes ());
63                 }
64
65                 public XElement (XName name)
66                 {
67                         this.name = name;
68                 }
69
70                 public XElement (XName name, params object [] contents)
71                 {
72                         this.name = name;
73                         Add (contents);
74                 }
75
76                 public XElement (XStreamingElement source)
77                 {
78                         this.name = source.Name;
79                         Add (source.Contents);
80                 }
81
82                 public static explicit operator bool (XElement element)
83                 {
84                         if (element == null)
85                                 throw new ArgumentNullException ("element");
86                         return XmlConvert.ToBoolean (element.Value);
87                 }
88
89                 public static explicit operator bool? (XElement element)
90                 {
91                         if (element == null)
92                                 return null;
93                         
94                         return element.Value == null ? (bool?) null : XmlConvert.ToBoolean (element.Value);
95                 }
96
97                 public static explicit operator DateTime (XElement element)
98                 {
99                         if (element == null)
100                                 throw new ArgumentNullException ("element");
101                         return XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
102                 }
103
104                 public static explicit operator DateTime? (XElement element)
105                 {
106                         if (element == null)
107                                 return null;
108                         
109                         return element.Value == null ? (DateTime?) null : XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
110                 }
111
112 #if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
113
114                 public static explicit operator DateTimeOffset (XElement element)
115                 {
116                         if (element == null)
117                                 throw new ArgumentNullException ("element");
118                         return XmlConvert.ToDateTimeOffset (element.Value);
119                 }
120
121                 public static explicit operator DateTimeOffset? (XElement element)
122                 {
123                         if (element == null)
124                                 return null;
125                         
126                         return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
127                 }
128
129 #endif
130
131                 public static explicit operator decimal (XElement element)
132                 {
133                         if (element == null)
134                                 throw new ArgumentNullException ("element");
135                         return XmlConvert.ToDecimal (element.Value);
136                 }
137
138                 public static explicit operator decimal? (XElement element)
139                 {
140                         if (element == null)
141                                 return null;
142                         
143                         return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
144                 }
145
146                 public static explicit operator double (XElement element)
147                 {
148                         if (element == null)
149                                 throw new ArgumentNullException ("element");
150                         return XmlConvert.ToDouble (element.Value);
151                 }
152
153                 public static explicit operator double? (XElement element)
154                 {
155                         if (element == null)
156                                 return null;
157                         
158                         return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
159                 }
160
161                 public static explicit operator float (XElement element)
162                 {
163                         if (element == null)
164                                 throw new ArgumentNullException ("element");
165                         return XmlConvert.ToSingle (element.Value);
166                 }
167
168                 public static explicit operator float? (XElement element)
169                 {
170                         if (element == null)
171                                 return null;
172                         
173                         return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
174                 }
175
176                 public static explicit operator Guid (XElement element)
177                 {
178                         if (element == null)
179                                 throw new ArgumentNullException ("element");
180                         return XmlConvert.ToGuid (element.Value);
181                 }
182
183                 public static explicit operator Guid? (XElement element)
184                 {
185                         if (element == null)
186                                 return null;
187                         
188                         return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
189                 }
190
191                 public static explicit operator int (XElement element)
192                 {
193                         if (element == null)
194                                 throw new ArgumentNullException ("element");
195                         return XmlConvert.ToInt32 (element.Value);
196                 }
197
198                 public static explicit operator int? (XElement element)
199                 {
200                         if (element == null)
201                                 return null;
202                         
203                         return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
204                 }
205
206                 public static explicit operator long (XElement element)
207                 {
208                         if (element == null)
209                                 throw new ArgumentNullException ("element");
210                         return XmlConvert.ToInt64 (element.Value);
211                 }
212
213                 public static explicit operator long? (XElement element)
214                 {
215                         if (element == null)
216                                 return null;
217                         
218                         return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
219                 }
220
221                 [CLSCompliant (false)]
222                 public static explicit operator uint (XElement element)
223                 {
224                         if (element == null)
225                                 throw new ArgumentNullException ("element");
226                         return XmlConvert.ToUInt32 (element.Value);
227                 }
228
229                 [CLSCompliant (false)]
230                 public static explicit operator uint? (XElement element)
231                 {
232                         if (element == null)
233                                 return null;
234                         
235                         return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
236                 }
237
238                 [CLSCompliant (false)]
239                 public static explicit operator ulong (XElement element)
240                 {
241                         if (element == null)
242                                 throw new ArgumentNullException ("element");
243                         return XmlConvert.ToUInt64 (element.Value);
244                 }
245
246                 [CLSCompliant (false)]
247                 public static explicit operator ulong? (XElement element)
248                 {
249                         if (element == null)
250                                 return null;
251                         
252                         return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
253                 }
254
255                 public static explicit operator TimeSpan (XElement element)
256                 {
257                         if (element == null)
258                                 throw new ArgumentNullException ("element");
259                         return XmlConvert.ToTimeSpan (element.Value);
260                 }
261
262                 public static explicit operator TimeSpan? (XElement element)
263                 {
264                         if (element == null)
265                                 return null;
266                         
267                         return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
268                 }
269
270                 public static explicit operator string (XElement element)
271                 {
272                         if (element == null)
273                                 return null;
274                         
275                         return element.Value;
276                 }
277
278                 public XAttribute FirstAttribute {
279                         get { return attr_first; }
280                         internal set { attr_first = value; }
281                 }
282
283                 public XAttribute LastAttribute {
284                         get { return attr_last; }
285                         internal set { attr_last = value; }
286                 }
287
288                 public bool HasAttributes {
289                         get { return attr_first != null; }
290                 }
291
292                 public bool HasElements {
293                         get {
294                                 foreach (object o in Nodes ())
295                                         if (o is XElement)
296                                                 return true;
297                                 return false;
298                         }
299                 }
300
301                 public bool IsEmpty {
302                         get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
303                         internal set { explicit_is_empty = value; }
304                 }
305
306                 public XName Name {
307                         get { return name; }
308                         set {
309                                 if (name == null)
310                                         throw new ArgumentNullException ("value");
311                                 name = value;
312                         }
313                 }
314
315                 public override XmlNodeType NodeType {
316                         get { return XmlNodeType.Element; }
317                 }
318
319                 public string Value {
320                         get {
321                                 StringBuilder sb = null;
322                                 foreach (XNode n in Nodes ()) {
323                                         if (sb == null)
324                                                 sb = new StringBuilder ();
325                                         if (n is XText)
326                                                 sb.Append (((XText) n).Value);
327                                         else if (n is XElement)
328                                                 sb.Append (((XElement) n).Value);
329                                 }
330                                 return sb == null ? String.Empty : sb.ToString ();
331                         }
332                         set {
333                                 RemoveNodes ();
334                                 Add (value);
335                         }
336                 }
337
338                 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
339                 {
340                         List <XElement> list = new List <XElement> ();
341                         if (getMeIn)
342                                 list.Add (this);
343                         for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
344                                 if (name == null || el.Name == name)
345                                         list.Add (el);
346                         return list;
347                 }
348
349                 public XAttribute Attribute (XName name)
350                 {
351                         foreach (XAttribute a in Attributes ())
352                                 if (a.Name == name)
353                                         return a;
354                         return null;
355                 }
356
357                 public IEnumerable <XAttribute> Attributes ()
358                 {
359                         XAttribute next;
360                         for (XAttribute a = attr_first; a != null; a = next) {
361                                 next = a.NextAttribute;
362                                 yield return a;
363                         }
364                 }
365
366                 // huh?
367                 public IEnumerable <XAttribute> Attributes (XName name)
368                 {
369                         foreach (XAttribute a in Attributes ())
370                                 if (a.Name == name)
371                                         yield return a;
372                 }
373
374                 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
375                 {
376 #if NET_2_1 && !MONOTOUCH
377                         // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
378                         settings.DtdProcessing = DtdProcessing.Parse;
379 #else
380                         settings.ProhibitDtd = false;
381 #endif
382
383                         settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
384                 }
385
386                 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
387                 {
388                         var settings = new XmlReaderSettings ();
389                         DefineDefaultSettings (settings, options);
390                         return settings;
391                 }
392
393                 public static XElement Load (string uri)
394                 {
395                         return Load (uri, LoadOptions.None);
396                 }
397
398                 public static XElement Load (string uri, LoadOptions options)
399                 {
400                         XmlReaderSettings s = CreateDefaultSettings (options);
401
402                         using (XmlReader r = XmlReader.Create (uri, s)) {
403                                 return LoadCore (r, options);
404                         }
405                 }
406
407                 public static XElement Load (TextReader tr)
408                 {
409                         return Load (tr, LoadOptions.None);
410                 }
411
412                 public static XElement Load (TextReader tr, LoadOptions options)
413                 {
414                         XmlReaderSettings s = CreateDefaultSettings (options);
415
416                         using (XmlReader r = XmlReader.Create (tr, s)) {
417                                 return LoadCore (r, options);
418                         }
419                 }
420
421                 public static XElement Load (XmlReader reader)
422                 {
423                         return Load (reader, LoadOptions.None);
424                 }
425
426                 public static XElement Load (XmlReader reader, LoadOptions options)
427                 {
428                         XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
429                         DefineDefaultSettings (s, options);
430
431                         using (XmlReader r = XmlReader.Create (reader, s)) {
432                                 return LoadCore (r, options);
433                         }
434                 }
435
436                 internal static XElement LoadCore (XmlReader r, LoadOptions options)
437                 {
438                         r.MoveToContent ();
439                         if (r.NodeType != XmlNodeType.Element)
440                                 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
441                         XName name = XName.Get (r.LocalName, r.NamespaceURI);
442                         XElement e = new XElement (name);
443                         e.FillLineInfoAndBaseUri (r, options);
444
445                         if (r.MoveToFirstAttribute ()) {
446                                 do {
447                                         // not sure how current Orcas behavior makes sense here though ...
448                                         if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
449                                                 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
450                                         else
451                                                 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
452                                         e.LastAttribute.FillLineInfoAndBaseUri (r, options);
453                                 } while (r.MoveToNextAttribute ());
454                                 r.MoveToElement ();
455                         }
456                         if (!r.IsEmptyElement) {
457                                 r.Read ();
458                                 e.ReadContentFrom (r, options);
459                                 r.ReadEndElement ();
460                                 e.explicit_is_empty = false;
461                         } else {
462                                 e.explicit_is_empty = true;
463                                 r.Read ();
464                         }
465                         return e;
466                 }
467
468                 public static XElement Parse (string s)
469                 {
470                         return Parse (s, LoadOptions.None);
471                 }
472
473                 public static XElement Parse (string s, LoadOptions options)
474                 {
475                         return Load (new StringReader (s), options);
476                 }
477
478                 public void RemoveAll ()
479                 {
480                         RemoveAttributes ();
481                         RemoveNodes ();
482                 }
483
484                 public void RemoveAttributes ()
485                 {
486                         while (attr_first != null)
487                                 attr_last.Remove ();
488                 }
489
490                 public void Save (string filename)
491                 {
492                         Save (filename, SaveOptions.None);
493                 }
494
495                 public void Save (string filename, SaveOptions options)
496                 {
497                         XmlWriterSettings s = new XmlWriterSettings ();
498                         s.Indent = options != SaveOptions.DisableFormatting;
499                         using (XmlWriter w = XmlWriter.Create (filename, s)) {
500                                 Save (w);
501                         }
502                 }
503
504                 public void Save (TextWriter tw)
505                 {
506                         Save (tw, SaveOptions.None);
507                 }
508
509                 public void Save (TextWriter tw, SaveOptions options)
510                 {
511                         XmlWriterSettings s = new XmlWriterSettings ();
512                         s.Indent = options != SaveOptions.DisableFormatting;
513                         using (XmlWriter w = XmlWriter.Create (tw, s)) {
514                                 Save (w);
515                         }
516                 }
517
518                 public void Save (XmlWriter w)
519                 {
520                         WriteTo (w);
521                 }
522
523                 public IEnumerable <XElement> AncestorsAndSelf ()
524                 {
525                         return GetAncestorList (null, true);
526                 }
527
528                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
529                 {
530                         return GetAncestorList (name, true);
531                 }
532
533                 public IEnumerable <XElement> DescendantsAndSelf ()
534                 {
535                         List <XElement> list = new List <XElement> ();
536                         list.Add (this);
537                         list.AddRange (Descendants ());
538                         return list;
539                 }
540
541                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
542                 {
543                         List <XElement> list = new List <XElement> ();
544                         if (name == this.name)
545                                 list.Add (this);
546                         list.AddRange (Descendants (name));
547                         return list;
548                 }
549
550                 public IEnumerable <XNode> DescendantNodesAndSelf ()
551                 {
552                         yield return this;
553                         foreach (XNode node in DescendantNodes ())
554                                 yield return node;
555                 }
556
557                 public void SetAttributeValue (XName name, object value)
558                 {
559                         XAttribute a = Attribute (name);
560                         if (value == null) {
561                                 if (a != null)
562                                         a.Remove ();
563                         } else {
564                                 if (a == null) {
565                                         SetAttributeObject (new XAttribute (name, value));
566                                 }
567                                 else
568                                         a.Value = XUtil.ToString (value);
569                         }
570                 }
571
572                 void SetAttributeObject (XAttribute a)
573                 {
574                         a = (XAttribute) XUtil.GetDetachedObject (a);
575                         a.SetOwner (this);
576                         if (attr_first == null) {
577                                 attr_first = a;
578                                 attr_last = a;
579                         } else {
580                                 attr_last.NextAttribute = a;
581                                 a.PreviousAttribute = attr_last;
582                                 attr_last = a;
583                         }
584                 }
585
586                 public override void WriteTo (XmlWriter w)
587                 {
588                         // some people expect the same prefix output as in input,
589                         // in the loss of performance... see bug #466423.
590                         string prefix = name.NamespaceName.Length > 0 ? w.LookupPrefix (name.Namespace.NamespaceName) : String.Empty;
591                         foreach (XAttribute a in Attributes ()) {
592                                 if (a.IsNamespaceDeclaration && a.Value == name.Namespace.NamespaceName) {
593                                         if (a.Name.Namespace == XNamespace.Xmlns)
594                                                 prefix = a.Name.LocalName;
595                                         // otherwise xmlns="..."
596                                         break;
597                                 }
598                         }
599
600                         w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
601
602                         foreach (XAttribute a in Attributes ()) {
603                                 if (a.IsNamespaceDeclaration) {
604                                         if (a.Name.Namespace == XNamespace.Xmlns)
605                                                 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
606                                         else
607                                                 w.WriteAttributeString ("xmlns", a.Value);
608                                 }
609                                 else
610                                         w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
611                         }
612
613                         foreach (XNode node in Nodes ())
614                                 node.WriteTo (w);
615
616                         if (explicit_is_empty)
617                                 w.WriteEndElement ();
618                         else
619                                 w.WriteFullEndElement ();
620                 }
621
622                 public XNamespace GetDefaultNamespace ()
623                 {
624                         for (XElement el = this; el != null; el = el.Parent)
625                                 foreach (XAttribute a in el.Attributes ())
626                                         if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
627                                                 return XNamespace.Get (a.Value);
628                         return XNamespace.None; // nothing is declared.
629                 }
630
631                 public XNamespace GetNamespaceOfPrefix (string prefix)
632                 {
633                         for (XElement el = this; el != null; el = el.Parent)
634                                 foreach (XAttribute a in el.Attributes ())
635                                         if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
636                                                 return XNamespace.Get (a.Value);
637                         return XNamespace.None; // nothing is declared.
638                 }
639
640                 public string GetPrefixOfNamespace (XNamespace ns)
641                 {
642                         foreach (string prefix in GetPrefixOfNamespaceCore (ns))
643                                 if (GetNamespaceOfPrefix (prefix) == ns)
644                                         return prefix;
645                         return null; // nothing is declared
646                 }
647                 
648                 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
649                 {
650                         for (XElement el = this; el != null; el = el.Parent)
651                                 foreach (XAttribute a in el.Attributes ())
652                                         if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
653                                                 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
654                 }
655
656                 public void ReplaceAll (object item)
657                 {
658                         RemoveNodes ();
659                         Add (item);
660                 }
661
662                 public void ReplaceAll (params object [] items)
663                 {
664                         RemoveNodes ();
665                         Add (items);
666                 }
667
668                 public void ReplaceAttributes (object item)
669                 {
670                         RemoveAttributes ();
671                         Add (item);
672                 }
673
674                 public void ReplaceAttributes (params object [] items)
675                 {
676                         RemoveAttributes ();
677                         Add (items);
678                 }
679
680                 public void SetElementValue (XName name, object value)
681                 {
682                         XElement el = new XElement (name, value);
683                         RemoveNodes ();
684                         Add (el);
685                 }
686
687                 public void SetValue (object value)
688                 {
689                         if (value == null)
690                                 throw new ArgumentNullException ("value");
691                         if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
692                                 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
693                         RemoveNodes ();
694                         foreach (object o in XUtil.ExpandArray (value))
695                                 Add (o);
696                 }
697
698                 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
699                 {
700                         if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
701                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
702
703                         XAttribute a = o as XAttribute;
704                         if (a != null) {
705                                 foreach (XAttribute ia in Attributes ())
706                                         if (a.Name == ia.Name)
707                                                 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
708                                 SetAttributeObject (a);
709                                 return true;
710                         }
711                         else if (o is string && refNode is XText) {
712                                 ((XText) refNode).Value += o as string;
713                                 return true;
714                         }
715                         else
716                                 return false;
717                 }
718
719                 void IXmlSerializable.WriteXml (XmlWriter writer)
720                 {
721                         Save (writer);
722                 }
723
724                 void IXmlSerializable.ReadXml (XmlReader reader)
725                 {
726                         ReadContentFrom (reader, LoadOptions.None);
727                 }
728
729                 XmlSchema IXmlSerializable.GetSchema ()
730                 {
731                         return null;
732                 }
733         }
734 }