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