2008-05-12 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 (XNode n in Nodes ()) {
303                                         if (sb == null)
304                                                 sb = new StringBuilder ();
305                                         if (n is XText)
306                                                 sb.Append (((XText) n).Value);
307                                         else if (n is XElement)
308                                                 sb.Append (((XElement) n).Value);
309                                 }
310                                 return sb == null ? String.Empty : sb.ToString ();
311                         }
312                         set {
313                                 RemoveNodes ();
314                                 Add (value);
315                         }
316                 }
317
318                 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
319                 {
320                         List <XElement> list = new List <XElement> ();
321                         if (getMeIn)
322                                 list.Add (this);
323                         for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
324                                 if (name == null || el.Name == name)
325                                         list.Add (el);
326                         return list;
327                 }
328
329                 public XAttribute Attribute (XName name)
330                 {
331                         foreach (XAttribute a in Attributes ())
332                                 if (a.Name == name)
333                                         return a;
334                         return null;
335                 }
336
337                 public IEnumerable <XAttribute> Attributes ()
338                 {
339                         XAttribute next;
340                         for (XAttribute a = attr_first; a != null; a = next) {
341                                 next = a.NextAttribute;
342                                 yield return a;
343                         }
344                 }
345
346                 // huh?
347                 public IEnumerable <XAttribute> Attributes (XName name)
348                 {
349                         foreach (XAttribute a in Attributes ())
350                                 if (a.Name == name)
351                                         yield return a;
352                 }
353
354                 public static XElement Load (string uri)
355                 {
356                         return Load (uri, LoadOptions.None);
357                 }
358
359                 public static XElement Load (string uri, LoadOptions options)
360                 {
361                         XmlReaderSettings s = new XmlReaderSettings ();
362                         s.ProhibitDtd = false;
363                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
364                         using (XmlReader r = XmlReader.Create (uri, s)) {
365                                 return LoadCore (r, options);
366                         }
367                 }
368
369                 public static XElement Load (TextReader tr)
370                 {
371                         return Load (tr, LoadOptions.None);
372                 }
373
374                 public static XElement Load (TextReader tr, LoadOptions options)
375                 {
376                         XmlReaderSettings s = new XmlReaderSettings ();
377                         s.ProhibitDtd = false;
378                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
379                         using (XmlReader r = XmlReader.Create (tr, s)) {
380                                 return LoadCore (r, options);
381                         }
382                 }
383
384                 public static XElement Load (XmlReader reader)
385                 {
386                         return Load (reader, LoadOptions.None);
387                 }
388
389                 public static XElement Load (XmlReader reader, LoadOptions options)
390                 {
391                         XmlReaderSettings s = reader.Settings.Clone ();
392                         s.ProhibitDtd = false;
393                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
394                         using (XmlReader r = XmlReader.Create (reader, s)) {
395                                 return LoadCore (r, options);
396                         }
397                 }
398
399                 internal static XElement LoadCore (XmlReader r, LoadOptions options)
400                 {
401                         r.MoveToContent ();
402                         if (r.NodeType != XmlNodeType.Element)
403                                 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
404                         XName name = XName.Get (r.LocalName, r.NamespaceURI);
405                         XElement e = new XElement (name);
406                         e.FillLineInfoAndBaseUri (r, options);
407
408                         if (r.MoveToFirstAttribute ()) {
409                                 do {
410                                         // not sure how current Orcas behavior makes sense here though ...
411                                         if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
412                                                 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
413                                         else
414                                                 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
415                                         e.LastAttribute.FillLineInfoAndBaseUri (r, options);
416                                 } while (r.MoveToNextAttribute ());
417                                 r.MoveToElement ();
418                         }
419                         if (!r.IsEmptyElement) {
420                                 r.Read ();
421                                 e.ReadContentFrom (r, options);
422                                 r.ReadEndElement ();
423                                 e.explicit_is_empty = false;
424                         } else {
425                                 e.explicit_is_empty = true;
426                                 r.Read ();
427                         }
428                         return e;
429                 }
430
431                 public static XElement Parse (string s)
432                 {
433                         return Parse (s, LoadOptions.None);
434                 }
435
436                 public static XElement Parse (string s, LoadOptions options)
437                 {
438                         return Load (new StringReader (s), options);
439                 }
440
441                 public void RemoveAll ()
442                 {
443                         RemoveAttributes ();
444                         RemoveNodes ();
445                 }
446
447                 public void RemoveAttributes ()
448                 {
449                         while (attr_first != null)
450                                 attr_last.Remove ();
451                 }
452
453                 public void Save (string filename)
454                 {
455                         Save (filename, SaveOptions.None);
456                 }
457
458                 public void Save (string filename, SaveOptions options)
459                 {
460                         XmlWriterSettings s = new XmlWriterSettings ();
461                         s.Indent = options != SaveOptions.DisableFormatting;
462                         using (XmlWriter w = XmlWriter.Create (filename, s)) {
463                                 Save (w);
464                         }
465                 }
466
467                 public void Save (TextWriter tw)
468                 {
469                         Save (tw, SaveOptions.None);
470                 }
471
472                 public void Save (TextWriter tw, SaveOptions options)
473                 {
474                         XmlWriterSettings s = new XmlWriterSettings ();
475                         s.Indent = options != SaveOptions.DisableFormatting;
476                         using (XmlWriter w = XmlWriter.Create (tw, s)) {
477                                 Save (w);
478                         }
479                 }
480
481                 public void Save (XmlWriter w)
482                 {
483                         WriteTo (w);
484                 }
485
486                 public IEnumerable <XElement> AncestorsAndSelf ()
487                 {
488                         return GetAncestorList (null, true);
489                 }
490
491                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
492                 {
493                         return GetAncestorList (name, true);
494                 }
495
496                 public IEnumerable <XElement> DescendantsAndSelf ()
497                 {
498                         List <XElement> list = new List <XElement> ();
499                         list.Add (this);
500                         list.AddRange (Descendants ());
501                         return list;
502                 }
503
504                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
505                 {
506                         List <XElement> list = new List <XElement> ();
507                         if (name == this.name)
508                                 list.Add (this);
509                         list.AddRange (Descendants (name));
510                         return list;
511                 }
512
513                 public IEnumerable <XNode> DescendantNodesAndSelf ()
514                 {
515                         yield return this;
516                         foreach (XNode node in DescendantNodes ())
517                                 yield return node;
518                 }
519
520                 public void SetAttributeValue (XName name, object value)
521                 {
522                         XAttribute a = Attribute (name);
523                         if (value == null) {
524                                 if (a != null)
525                                         a.Remove ();
526                         } else {
527                                 if (a == null) {
528                                         SetAttributeObject (new XAttribute (name, value));
529                                 }
530                                 else
531                                         a.Value = XUtil.ToString (value);
532                         }
533                 }
534
535                 void SetAttributeObject (XAttribute a)
536                 {
537                         a.SetOwner (this);
538                         if (attr_first == null) {
539                                 attr_first = a;
540                                 attr_last = a;
541                         } else {
542                                 attr_last.NextAttribute = a;
543                                 a.PreviousAttribute = attr_last;
544                                 attr_last = a;
545                         }
546                 }
547
548                 public override void WriteTo (XmlWriter w)
549                 {
550                         w.WriteStartElement (name.LocalName, name.Namespace.NamespaceName);
551
552                         foreach (XAttribute a in Attributes ()) {
553                                 if (a.IsNamespaceDeclaration) {
554                                         if (a.Name.Namespace == XNamespace.Xmlns)
555                                                 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
556                                         else
557                                                 w.WriteAttributeString ("xmlns", a.Value);
558                                 }
559                                 else
560                                         w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
561                         }
562
563                         foreach (XNode node in Nodes ())
564                                 node.WriteTo (w);
565
566                         if (explicit_is_empty)
567                                 w.WriteEndElement ();
568                         else
569                                 w.WriteFullEndElement ();
570                 }
571
572                 public XNamespace GetDefaultNamespace ()
573                 {
574                         for (XElement el = this; el != null; el = el.Parent)
575                                 foreach (XAttribute a in el.Attributes ())
576                                         if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
577                                                 return XNamespace.Get (a.Value);
578                         return XNamespace.None; // nothing is declared.
579                 }
580
581                 public XNamespace GetNamespaceOfPrefix (string prefix)
582                 {
583                         for (XElement el = this; el != null; el = el.Parent)
584                                 foreach (XAttribute a in el.Attributes ())
585                                         if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix)
586                                                 return XNamespace.Get (a.Value);
587                         return XNamespace.None; // nothing is declared.
588                 }
589
590                 public string GetPrefixOfNamespace (XNamespace ns)
591                 {
592                         foreach (string prefix in GetPrefixOfNamespaceCore (ns))
593                                 if (GetNamespaceOfPrefix (prefix) == ns)
594                                         return prefix;
595                         return null; // nothing is declared
596                 }
597                 
598                 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
599                 {
600                         for (XElement el = this; el != null; el = el.Parent)
601                                 foreach (XAttribute a in el.Attributes ())
602                                         if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
603                                                 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
604                 }
605
606                 public void ReplaceAll (object item)
607                 {
608                         RemoveNodes ();
609                         Add (item);
610                 }
611
612                 public void ReplaceAll (params object [] items)
613                 {
614                         RemoveNodes ();
615                         Add (items);
616                 }
617
618                 public void ReplaceAttributes (object item)
619                 {
620                         RemoveAttributes ();
621                         Add (item);
622                 }
623
624                 public void ReplaceAttributes (params object [] items)
625                 {
626                         RemoveAttributes ();
627                         Add (items);
628                 }
629
630                 public void SetElementValue (XName name, object value)
631                 {
632                         XElement el = new XElement (name, value);
633                         RemoveNodes ();
634                         Add (el);
635                 }
636
637                 public void SetValue (object value)
638                 {
639                         if (value == null)
640                                 throw new ArgumentNullException ("value");
641                         if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
642                                 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
643                         RemoveNodes ();
644                         foreach (XNode n in XUtil.ToNodes (value))
645                                 Add (n);
646                 }
647
648                 internal override bool OnAddingObject (object o)
649                 {
650                         XAttribute a = o as XAttribute;
651                         if (a != null) {
652                                 foreach (XAttribute ia in Attributes ())
653                                         if (a.Name == ia.Name)
654                                                 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
655                                 SetAttributeObject (a);
656                                 return true;
657                         }
658                         else if (o is string && LastNode is XText) {
659                                 ((XText) LastNode).Value += o as string;
660                                 return true;
661                         }
662                         else
663                                 return false;
664                 }
665
666                 internal override void OnAdded (XNode node, bool addFirst)
667                 {
668                         if (node is XDocument || node is XDocumentType || node is XDeclaration)
669                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", node.GetType ()));
670                 }
671
672                 void IXmlSerializable.WriteXml (XmlWriter writer)
673                 {
674                         Save (writer);
675                 }
676
677                 void IXmlSerializable.ReadXml (XmlReader reader)
678                 {
679                         ReadContentFrom (reader, LoadOptions.None);
680                 }
681
682                 XmlSchema IXmlSerializable.GetSchema ()
683                 {
684                         return null;
685                 }
686         }
687 }