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                 [MonoTODO ("finish")]           
76                 public XElement (XStreamingElement source)
77                 {
78                         this.name = source.Name;
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                         if ((options & SaveOptions.DisableFormatting) != 0) {
459                                 // hacky!
460                                 s.Indent = true;
461                                 s.IndentChars = String.Empty;
462                                 s.NewLineChars = String.Empty;
463                         }
464                         using (XmlWriter w = XmlWriter.Create (filename)) {
465                                 Save (w);
466                         }
467                 }
468
469                 public void Save (TextWriter tw)
470                 {
471                         Save (tw, SaveOptions.None);
472                 }
473
474                 public void Save (TextWriter tw, SaveOptions options)
475                 {
476                         XmlWriterSettings s = new XmlWriterSettings ();
477                         if ((options & SaveOptions.DisableFormatting) != 0) {
478                                 // hacky!
479                                 s.Indent = true;
480                                 s.IndentChars = String.Empty;
481                                 s.NewLineChars = String.Empty;
482                         }
483                         using (XmlWriter w = XmlWriter.Create (tw)) {
484                                 Save (w);
485                         }
486                 }
487
488                 public void Save (XmlWriter w)
489                 {
490                         WriteTo (w);
491                 }
492
493                 public IEnumerable <XElement> AncestorsAndSelf ()
494                 {
495                         return GetAncestorList (null, true);
496                 }
497
498                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
499                 {
500                         return GetAncestorList (name, true);
501                 }
502
503                 public IEnumerable <XElement> DescendantsAndSelf ()
504                 {
505                         List <XElement> list = new List <XElement> ();
506                         list.Add (this);
507                         list.AddRange (Descendants ());
508                         return list;
509                 }
510
511                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
512                 {
513                         List <XElement> list = new List <XElement> ();
514                         if (name == this.name)
515                                 list.Add (this);
516                         list.AddRange (Descendants (name));
517                         return list;
518                 }
519
520                 public IEnumerable <XNode> DescendantNodesAndSelf ()
521                 {
522                         yield return this;
523                         foreach (XNode node in DescendantNodes ())
524                                 yield return node;
525                 }
526
527                 public void SetAttributeValue (XName name, object value)
528                 {
529                         XAttribute a = Attribute (name);
530                         if (value == null) {
531                                 if (a != null)
532                                         a.Remove ();
533                         } else {
534                                 if (a == null) {
535                                         a = new XAttribute (name, value);
536                                         a.SetOwner (this);
537                                         if (attr_first == null) {
538                                                 attr_first = a;
539                                                 attr_last = a;
540                                         } else {
541                                                 attr_last.NextAttribute = a;
542                                                 a.PreviousAttribute = attr_last;
543                                                 attr_last = a;
544                                         }
545                                 }
546                                 else
547                                         a.Value = XUtil.ToString (value);
548                         }
549                 }
550
551                 public override void WriteTo (XmlWriter w)
552                 {
553                         w.WriteStartElement (name.LocalName, name.Namespace.NamespaceName);
554
555                         foreach (XAttribute a in Attributes ()) {
556                                 if (a.IsNamespaceDeclaration) {
557                                         if (a.Name.Namespace == XNamespace.Xmlns)
558                                                 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
559                                         else
560                                                 w.WriteAttributeString ("xmlns", a.Value);
561                                 }
562                                 else
563                                         w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
564                         }
565
566                         foreach (XNode node in Nodes ())
567                                 node.WriteTo (w);
568
569                         if (explicit_is_empty)
570                                 w.WriteEndElement ();
571                         else
572                                 w.WriteFullEndElement ();
573                 }
574
575                 public XNamespace GetDefaultNamespace ()
576                 {
577                         for (XElement el = this; el != null; el = el.Parent)
578                                 foreach (XAttribute a in el.Attributes ())
579                                         if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
580                                                 return XNamespace.Get (a.Value);
581                         return XNamespace.None; // nothing is declared.
582                 }
583
584                 public XNamespace GetNamespaceOfPrefix (string prefix)
585                 {
586                         for (XElement el = this; el != null; el = el.Parent)
587                                 foreach (XAttribute a in el.Attributes ())
588                                         if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix)
589                                                 return XNamespace.Get (a.Value);
590                         return XNamespace.None; // nothing is declared.
591                 }
592
593                 public string GetPrefixOfNamespace (XNamespace ns)
594                 {
595                         foreach (string prefix in GetPrefixOfNamespaceCore (ns))
596                                 if (GetNamespaceOfPrefix (prefix) == ns)
597                                         return prefix;
598                         return null; // nothing is declared
599                 }
600                 
601                 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
602                 {
603                         for (XElement el = this; el != null; el = el.Parent)
604                                 foreach (XAttribute a in el.Attributes ())
605                                         if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
606                                                 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
607                 }
608
609                 public void ReplaceAll (object item)
610                 {
611                         RemoveNodes ();
612                         Add (item);
613                 }
614
615                 public void ReplaceAll (params object [] items)
616                 {
617                         RemoveNodes ();
618                         Add (items);
619                 }
620
621                 public void ReplaceAttributes (object item)
622                 {
623                         RemoveAttributes ();
624                         Add (item);
625                 }
626
627                 public void ReplaceAttributes (params object [] items)
628                 {
629                         RemoveAttributes ();
630                         Add (items);
631                 }
632
633                 public void SetElementValue (XName name, object value)
634                 {
635                         XElement el = new XElement (name, value);
636                         RemoveNodes ();
637                         Add (el);
638                 }
639
640                 public void SetValue (object value)
641                 {
642                         XNode n = XUtil.ToNode (value);
643                         RemoveNodes ();
644                         Add (n);
645                 }
646
647                 internal override void OnAdded (XNode node, bool addFirst)
648                 {
649                         if (node is XDocument || node is XDocumentType)
650                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", node.GetType ()));
651                 }
652
653                 void IXmlSerializable.WriteXml (XmlWriter writer)
654                 {
655                         Save (writer);
656                 }
657
658                 void IXmlSerializable.ReadXml (XmlReader reader)
659                 {
660                         ReadContentFrom (reader, LoadOptions.None);
661                 }
662
663                 XmlSchema IXmlSerializable.GetSchema ()
664                 {
665                         return null;
666                 }
667         }
668 }