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.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
360                         using (XmlReader r = XmlReader.Create (uri, s)) {
361                                 return LoadCore (r, options);
362                         }
363                 }
364
365                 public static XElement Load (TextReader tr)
366                 {
367                         return Load (tr, LoadOptions.None);
368                 }
369
370                 public static XElement Load (TextReader tr, LoadOptions options)
371                 {
372                         XmlReaderSettings s = new XmlReaderSettings ();
373                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
374                         using (XmlReader r = XmlReader.Create (tr, s)) {
375                                 return LoadCore (r, options);
376                         }
377                 }
378
379                 public static XElement Load (XmlReader reader)
380                 {
381                         return Load (reader, LoadOptions.None);
382                 }
383
384                 public static XElement Load (XmlReader reader, LoadOptions options)
385                 {
386                         XmlReaderSettings s = reader.Settings.Clone ();
387                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
388                         using (XmlReader r = XmlReader.Create (reader, s)) {
389                                 return LoadCore (r, options);
390                         }
391                 }
392
393                 static XElement LoadCore (XmlReader r, LoadOptions options)
394                 {
395                         r.MoveToContent ();
396                         if (r.NodeType != XmlNodeType.Element)
397                                 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
398                         XName name = XName.Get (r.LocalName, r.NamespaceURI);
399                         XElement e = new XElement (name);
400                         e.FillLineInfoAndBaseUri (r, options);
401
402                         if (r.MoveToFirstAttribute ()) {
403                                 do {
404                                         // not sure how current Orcas behavior makes sense here though ...
405                                         if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
406                                                 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
407                                         else
408                                                 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
409                                         e.LastAttribute.FillLineInfoAndBaseUri (r, options);
410                                 } while (r.MoveToNextAttribute ());
411                                 r.MoveToElement ();
412                         }
413                         if (!r.IsEmptyElement) {
414                                 r.Read ();
415                                 e.ReadContentFrom (r, options);
416                                 r.ReadEndElement ();
417                                 e.explicit_is_empty = false;
418                         } else {
419                                 e.explicit_is_empty = true;
420                                 r.Read ();
421                         }
422                         return e;
423                 }
424
425                 public static XElement Parse (string s)
426                 {
427                         return Parse (s, LoadOptions.None);
428                 }
429
430                 public static XElement Parse (string s, LoadOptions options)
431                 {
432                         return Load (new StringReader (s), options);
433                 }
434
435                 public void RemoveAll ()
436                 {
437                         RemoveAttributes ();
438                         RemoveNodes ();
439                 }
440
441                 public void RemoveAttributes ()
442                 {
443                         while (attr_first != null)
444                                 attr_last.Remove ();
445                 }
446
447                 public void Save (string filename)
448                 {
449                         Save (filename, SaveOptions.None);
450                 }
451
452                 public void Save (string filename, SaveOptions options)
453                 {
454                         XmlWriterSettings s = new XmlWriterSettings ();
455                         if ((options & SaveOptions.DisableFormatting) != 0) {
456                                 // hacky!
457                                 s.Indent = true;
458                                 s.IndentChars = String.Empty;
459                                 s.NewLineChars = String.Empty;
460                         }
461                         using (XmlWriter w = XmlWriter.Create (filename)) {
462                                 Save (w);
463                         }
464                 }
465
466                 public void Save (TextWriter tw)
467                 {
468                         Save (tw, SaveOptions.None);
469                 }
470
471                 public void Save (TextWriter tw, SaveOptions options)
472                 {
473                         XmlWriterSettings s = new XmlWriterSettings ();
474                         if ((options & SaveOptions.DisableFormatting) != 0) {
475                                 // hacky!
476                                 s.Indent = true;
477                                 s.IndentChars = String.Empty;
478                                 s.NewLineChars = String.Empty;
479                         }
480                         using (XmlWriter w = XmlWriter.Create (tw)) {
481                                 Save (w);
482                         }
483                 }
484
485                 public void Save (XmlWriter w)
486                 {
487                         WriteTo (w);
488                 }
489
490                 public IEnumerable <XElement> AncestorsAndSelf ()
491                 {
492                         return GetAncestorList (null, true);
493                 }
494
495                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
496                 {
497                         return GetAncestorList (name, true);
498                 }
499
500                 public IEnumerable <XElement> DescendantsAndSelf ()
501                 {
502                         List <XElement> list = new List <XElement> ();
503                         list.Add (this);
504                         list.AddRange (Descendants ());
505                         return list;
506                 }
507
508                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
509                 {
510                         List <XElement> list = new List <XElement> ();
511                         if (name == this.name)
512                                 list.Add (this);
513                         list.AddRange (Descendants (name));
514                         return list;
515                 }
516
517                 public IEnumerable <XNode> DescendantNodesAndSelf ()
518                 {
519                         yield return this;
520                         foreach (XNode node in DescendantNodes ())
521                                 yield return node;
522                 }
523
524                 public void SetAttributeValue (XName name, object value)
525                 {
526                         XAttribute a = Attribute (name);
527                         if (value == null) {
528                                 if (a != null)
529                                         a.Remove ();
530                         } else {
531                                 if (a == null) {
532                                         a = new XAttribute (name, value);
533                                         a.SetOwner (this);
534                                         if (attr_first == null) {
535                                                 attr_first = a;
536                                                 attr_last = a;
537                                         } else {
538                                                 attr_last.NextAttribute = a;
539                                                 a.PreviousAttribute = attr_last;
540                                                 attr_last = a;
541                                         }
542                                 }
543                                 else
544                                         a.Value = XUtil.ToString (value);
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                         XNode n = XUtil.ToNode (value);
640                         RemoveNodes ();
641                         Add (n);
642                 }
643
644                 internal override void OnAdded (XNode node, bool addFirst)
645                 {
646                         if (node is XDocument || node is XDocumentType)
647                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", node.GetType ()));
648                 }
649
650                 void IXmlSerializable.WriteXml (XmlWriter writer)
651                 {
652                         Save (writer);
653                 }
654
655                 void IXmlSerializable.ReadXml (XmlReader reader)
656                 {
657                         ReadContentFrom (reader, LoadOptions.None);
658                 }
659
660                 XmlSchema IXmlSerializable.GetSchema ()
661                 {
662                         return null;
663                 }
664         }
665 }