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