2010-03-12 Jb Evain <jbevain@novell.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.Attributes ());
62                         Add (source.Nodes ());
63                 }
64
65                 public XElement (XName name)
66                 {
67                         this.name = name;
68                 }
69
70                 public XElement (XName name, params object [] contents)
71                 {
72                         this.name = name;
73                         Add (contents);
74                 }
75
76                 public XElement (XStreamingElement source)
77                 {
78                         this.name = source.Name;
79                         Add (source.Contents);
80                 }
81
82                 [CLSCompliant (false)]
83                 public static explicit operator bool (XElement element)
84                 {
85                         if (element == null)
86                                 throw new ArgumentNullException ("element");
87                         return XUtil.ConvertToBoolean (element.Value);
88                 }
89
90                 [CLSCompliant (false)]
91                 public static explicit operator bool? (XElement element)
92                 {
93                         if (element == null)
94                                 return null;
95                         
96                         return element.Value == null ? (bool?) null : XUtil.ConvertToBoolean (element.Value);
97                 }
98
99                 [CLSCompliant (false)]
100                 public static explicit operator DateTime (XElement element)
101                 {
102                         if (element == null)
103                                 throw new ArgumentNullException ("element");
104                         return XUtil.ToDateTime (element.Value);
105                 }
106
107                 [CLSCompliant (false)]
108                 public static explicit operator DateTime? (XElement element)
109                 {
110                         if (element == null)
111                                 return null;
112                         
113                         return element.Value == null ? (DateTime?) null : XUtil.ToDateTime (element.Value);
114                 }
115
116 #if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
117
118                 [CLSCompliant (false)]
119                 public static explicit operator DateTimeOffset (XElement element)
120                 {
121                         if (element == null)
122                                 throw new ArgumentNullException ("element");
123                         return XmlConvert.ToDateTimeOffset (element.Value);
124                 }
125
126                 [CLSCompliant (false)]
127                 public static explicit operator DateTimeOffset? (XElement element)
128                 {
129                         if (element == null)
130                                 return null;
131                         
132                         return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
133                 }
134
135 #endif
136
137                 [CLSCompliant (false)]
138                 public static explicit operator decimal (XElement element)
139                 {
140                         if (element == null)
141                                 throw new ArgumentNullException ("element");
142                         return XmlConvert.ToDecimal (element.Value);
143                 }
144
145                 [CLSCompliant (false)]
146                 public static explicit operator decimal? (XElement element)
147                 {
148                         if (element == null)
149                                 return null;
150                         
151                         return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
152                 }
153
154                 [CLSCompliant (false)]
155                 public static explicit operator double (XElement element)
156                 {
157                         if (element == null)
158                                 throw new ArgumentNullException ("element");
159                         return XmlConvert.ToDouble (element.Value);
160                 }
161
162                 [CLSCompliant (false)]
163                 public static explicit operator double? (XElement element)
164                 {
165                         if (element == null)
166                                 return null;
167                         
168                         return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
169                 }
170
171                 [CLSCompliant (false)]
172                 public static explicit operator float (XElement element)
173                 {
174                         if (element == null)
175                                 throw new ArgumentNullException ("element");
176                         return XmlConvert.ToSingle (element.Value);
177                 }
178
179                 [CLSCompliant (false)]
180                 public static explicit operator float? (XElement element)
181                 {
182                         if (element == null)
183                                 return null;
184                         
185                         return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
186                 }
187
188                 [CLSCompliant (false)]
189                 public static explicit operator Guid (XElement element)
190                 {
191                         if (element == null)
192                                 throw new ArgumentNullException ("element");
193                         return XmlConvert.ToGuid (element.Value);
194                 }
195
196                 [CLSCompliant (false)]
197                 public static explicit operator Guid? (XElement element)
198                 {
199                         if (element == null)
200                                 return null;
201                         
202                         return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
203                 }
204
205                 [CLSCompliant (false)]
206                 public static explicit operator int (XElement element)
207                 {
208                         if (element == null)
209                                 throw new ArgumentNullException ("element");
210                         return XmlConvert.ToInt32 (element.Value);
211                 }
212
213                 [CLSCompliant (false)]
214                 public static explicit operator int? (XElement element)
215                 {
216                         if (element == null)
217                                 return null;
218                         
219                         return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
220                 }
221
222                 [CLSCompliant (false)]
223                 public static explicit operator long (XElement element)
224                 {
225                         if (element == null)
226                                 throw new ArgumentNullException ("element");
227                         return XmlConvert.ToInt64 (element.Value);
228                 }
229
230                 [CLSCompliant (false)]
231                 public static explicit operator long? (XElement element)
232                 {
233                         if (element == null)
234                                 return null;
235                         
236                         return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
237                 }
238
239                 [CLSCompliant (false)]
240                 public static explicit operator uint (XElement element)
241                 {
242                         if (element == null)
243                                 throw new ArgumentNullException ("element");
244                         return XmlConvert.ToUInt32 (element.Value);
245                 }
246
247                 [CLSCompliant (false)]
248                 public static explicit operator uint? (XElement element)
249                 {
250                         if (element == null)
251                                 return null;
252                         
253                         return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
254                 }
255
256                 [CLSCompliant (false)]
257                 public static explicit operator ulong (XElement element)
258                 {
259                         if (element == null)
260                                 throw new ArgumentNullException ("element");
261                         return XmlConvert.ToUInt64 (element.Value);
262                 }
263
264                 [CLSCompliant (false)]
265                 public static explicit operator ulong? (XElement element)
266                 {
267                         if (element == null)
268                                 return null;
269                         
270                         return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
271                 }
272
273                 [CLSCompliant (false)]
274                 public static explicit operator TimeSpan (XElement element)
275                 {
276                         if (element == null)
277                                 throw new ArgumentNullException ("element");
278                         return XmlConvert.ToTimeSpan (element.Value);
279                 }
280
281                 [CLSCompliant (false)]
282                 public static explicit operator TimeSpan? (XElement element)
283                 {
284                         if (element == null)
285                                 return null;
286                         
287                         return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
288                 }
289
290                 [CLSCompliant (false)]
291                 public static explicit operator string (XElement element)
292                 {
293                         if (element == null)
294                                 return null;
295                         
296                         return element.Value;
297                 }
298
299                 public XAttribute FirstAttribute {
300                         get { return attr_first; }
301                         internal set { attr_first = value; }
302                 }
303
304                 public XAttribute LastAttribute {
305                         get { return attr_last; }
306                         internal set { attr_last = value; }
307                 }
308
309                 public bool HasAttributes {
310                         get { return attr_first != null; }
311                 }
312
313                 public bool HasElements {
314                         get {
315                                 foreach (object o in Nodes ())
316                                         if (o is XElement)
317                                                 return true;
318                                 return false;
319                         }
320                 }
321
322                 public bool IsEmpty {
323                         get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
324                         internal set { explicit_is_empty = value; }
325                 }
326
327                 public XName Name {
328                         get { return name; }
329                         set {
330                                 if (name == null)
331                                         throw new ArgumentNullException ("value");
332                                 name = value;
333                         }
334                 }
335
336                 public override XmlNodeType NodeType {
337                         get { return XmlNodeType.Element; }
338                 }
339
340                 public string Value {
341                         get {
342                                 StringBuilder sb = null;
343                                 foreach (XNode n in Nodes ()) {
344                                         if (sb == null)
345                                                 sb = new StringBuilder ();
346                                         if (n is XText)
347                                                 sb.Append (((XText) n).Value);
348                                         else if (n is XElement)
349                                                 sb.Append (((XElement) n).Value);
350                                 }
351                                 return sb == null ? String.Empty : sb.ToString ();
352                         }
353                         set {
354                                 RemoveNodes ();
355                                 Add (value);
356                         }
357                 }
358
359                 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
360                 {
361                         List <XElement> list = new List <XElement> ();
362                         if (getMeIn)
363                                 list.Add (this);
364                         for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
365                                 if (name == null || el.Name == name)
366                                         list.Add (el);
367                         return list;
368                 }
369
370                 public XAttribute Attribute (XName name)
371                 {
372                         foreach (XAttribute a in Attributes ())
373                                 if (a.Name == name)
374                                         return a;
375                         return null;
376                 }
377
378                 public IEnumerable <XAttribute> Attributes ()
379                 {
380                         XAttribute next;
381                         for (XAttribute a = attr_first; a != null; a = next) {
382                                 next = a.NextAttribute;
383                                 yield return a;
384                         }
385                 }
386
387                 // huh?
388                 public IEnumerable <XAttribute> Attributes (XName name)
389                 {
390                         foreach (XAttribute a in Attributes ())
391                                 if (a.Name == name)
392                                         yield return a;
393                 }
394
395                 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
396                 {
397 #if NET_2_1 && !MONOTOUCH
398                         // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
399                         settings.DtdProcessing = DtdProcessing.Parse;
400 #else
401                         settings.ProhibitDtd = false;
402 #endif
403
404                         settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
405                 }
406
407                 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
408                 {
409                         var settings = new XmlReaderSettings ();
410                         DefineDefaultSettings (settings, options);
411                         return settings;
412                 }
413
414                 public static XElement Load (string uri)
415                 {
416                         return Load (uri, LoadOptions.None);
417                 }
418
419                 public static XElement Load (string uri, LoadOptions options)
420                 {
421                         XmlReaderSettings s = CreateDefaultSettings (options);
422
423                         using (XmlReader r = XmlReader.Create (uri, s)) {
424                                 return LoadCore (r, options);
425                         }
426                 }
427
428                 public static XElement Load (TextReader tr)
429                 {
430                         return Load (tr, LoadOptions.None);
431                 }
432
433                 public static XElement Load (TextReader tr, LoadOptions options)
434                 {
435                         XmlReaderSettings s = CreateDefaultSettings (options);
436
437                         using (XmlReader r = XmlReader.Create (tr, s)) {
438                                 return LoadCore (r, options);
439                         }
440                 }
441
442                 public static XElement Load (XmlReader reader)
443                 {
444                         return Load (reader, LoadOptions.None);
445                 }
446
447                 public static XElement Load (XmlReader reader, LoadOptions options)
448                 {
449                         XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
450                         DefineDefaultSettings (s, options);
451
452                         using (XmlReader r = XmlReader.Create (reader, s)) {
453                                 return LoadCore (r, options);
454                         }
455                 }
456
457                 internal static XElement LoadCore (XmlReader r, LoadOptions options)
458                 {
459                         r.MoveToContent ();
460                         if (r.NodeType != XmlNodeType.Element)
461                                 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
462                         XName name = XName.Get (r.LocalName, r.NamespaceURI);
463                         XElement e = new XElement (name);
464                         e.FillLineInfoAndBaseUri (r, options);
465
466                         if (r.MoveToFirstAttribute ()) {
467                                 do {
468                                         // not sure how current Orcas behavior makes sense here though ...
469                                         if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
470                                                 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
471                                         else
472                                                 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
473                                         e.LastAttribute.FillLineInfoAndBaseUri (r, options);
474                                 } while (r.MoveToNextAttribute ());
475                                 r.MoveToElement ();
476                         }
477                         if (!r.IsEmptyElement) {
478                                 r.Read ();
479                                 e.ReadContentFrom (r, options);
480                                 r.ReadEndElement ();
481                                 e.explicit_is_empty = false;
482                         } else {
483                                 e.explicit_is_empty = true;
484                                 r.Read ();
485                         }
486                         return e;
487                 }
488
489                 public static XElement Parse (string s)
490                 {
491                         return Parse (s, LoadOptions.None);
492                 }
493
494                 public static XElement Parse (string s, LoadOptions options)
495                 {
496                         return Load (new StringReader (s), options);
497                 }
498
499                 public void RemoveAll ()
500                 {
501                         RemoveAttributes ();
502                         RemoveNodes ();
503                 }
504
505                 public void RemoveAttributes ()
506                 {
507                         while (attr_first != null)
508                                 attr_last.Remove ();
509                 }
510
511                 public void Save (string filename)
512                 {
513                         Save (filename, SaveOptions.None);
514                 }
515
516                 public void Save (string filename, SaveOptions options)
517                 {
518                         XmlWriterSettings s = new XmlWriterSettings ();
519
520                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
521                                 s.Indent = true;
522 #if NET_4_0
523                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
524                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
525 #endif
526                         using (XmlWriter w = XmlWriter.Create (filename, s)) {
527                                 Save (w);
528                         }
529                 }
530
531                 public void Save (TextWriter tw)
532                 {
533                         Save (tw, SaveOptions.None);
534                 }
535
536                 public void Save (TextWriter tw, SaveOptions options)
537                 {
538                         XmlWriterSettings s = new XmlWriterSettings ();
539                         
540                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
541                                 s.Indent = true;
542 #if NET_4_0
543                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
544                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
545 #endif
546                         using (XmlWriter w = XmlWriter.Create (tw, s)) {
547                                 Save (w);
548                         }
549                 }
550
551                 public void Save (XmlWriter w)
552                 {
553                         WriteTo (w);
554                 }
555
556 #if NET_4_0
557                 public void Save (Stream stream)
558                 {
559                         Save (stream, SaveOptions.None);
560                 }
561
562                 public void Save (Stream stream, SaveOptions options)
563                 {
564                         XmlWriterSettings s = new XmlWriterSettings ();
565                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
566                                 s.Indent = true;
567                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
568                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
569
570                         using (var writer = XmlWriter.Create (stream, s)){
571                                 Save (writer);
572                         }
573                 }
574 #endif
575                 public IEnumerable <XElement> AncestorsAndSelf ()
576                 {
577                         return GetAncestorList (null, true);
578                 }
579
580                 public IEnumerable <XElement> AncestorsAndSelf (XName name)
581                 {
582                         return GetAncestorList (name, true);
583                 }
584
585                 public IEnumerable <XElement> DescendantsAndSelf ()
586                 {
587                         List <XElement> list = new List <XElement> ();
588                         list.Add (this);
589                         list.AddRange (Descendants ());
590                         return list;
591                 }
592
593                 public IEnumerable <XElement> DescendantsAndSelf (XName name)
594                 {
595                         List <XElement> list = new List <XElement> ();
596                         if (name == this.name)
597                                 list.Add (this);
598                         list.AddRange (Descendants (name));
599                         return list;
600                 }
601
602                 public IEnumerable <XNode> DescendantNodesAndSelf ()
603                 {
604                         yield return this;
605                         foreach (XNode node in DescendantNodes ())
606                                 yield return node;
607                 }
608
609                 public void SetAttributeValue (XName name, object value)
610                 {
611                         XAttribute a = Attribute (name);
612                         if (value == null) {
613                                 if (a != null)
614                                         a.Remove ();
615                         } else {
616                                 if (a == null) {
617                                         SetAttributeObject (new XAttribute (name, value));
618                                 }
619                                 else
620                                         a.Value = XUtil.ToString (value);
621                         }
622                 }
623
624                 void SetAttributeObject (XAttribute a)
625                 {
626                         a = (XAttribute) XUtil.GetDetachedObject (a);
627                         a.SetOwner (this);
628                         if (attr_first == null) {
629                                 attr_first = a;
630                                 attr_last = a;
631                         } else {
632                                 attr_last.NextAttribute = a;
633                                 a.PreviousAttribute = attr_last;
634                                 attr_last = a;
635                         }
636                 }
637
638                 public override void WriteTo (XmlWriter w)
639                 {
640                         // some people expect the same prefix output as in input,
641                         // in the loss of performance... see bug #466423.
642                         string prefix = name.NamespaceName.Length > 0 ? w.LookupPrefix (name.Namespace.NamespaceName) : String.Empty;
643                         foreach (XAttribute a in Attributes ()) {
644                                 if (a.IsNamespaceDeclaration && a.Value == name.Namespace.NamespaceName) {
645                                         if (a.Name.Namespace == XNamespace.Xmlns)
646                                                 prefix = a.Name.LocalName;
647                                         // otherwise xmlns="..."
648                                         break;
649                                 }
650                         }
651
652                         w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
653
654                         foreach (XAttribute a in Attributes ()) {
655                                 if (a.IsNamespaceDeclaration) {
656                                         if (a.Name.Namespace == XNamespace.Xmlns)
657                                                 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
658                                         else
659                                                 w.WriteAttributeString ("xmlns", a.Value);
660                                 }
661                                 else
662                                         w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
663                         }
664
665                         foreach (XNode node in Nodes ())
666                                 node.WriteTo (w);
667
668                         if (explicit_is_empty)
669                                 w.WriteEndElement ();
670                         else
671                                 w.WriteFullEndElement ();
672                 }
673
674                 public XNamespace GetDefaultNamespace ()
675                 {
676                         for (XElement el = this; el != null; el = el.Parent)
677                                 foreach (XAttribute a in el.Attributes ())
678                                         if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
679                                                 return XNamespace.Get (a.Value);
680                         return XNamespace.None; // nothing is declared.
681                 }
682
683                 public XNamespace GetNamespaceOfPrefix (string prefix)
684                 {
685                         for (XElement el = this; el != null; el = el.Parent)
686                                 foreach (XAttribute a in el.Attributes ())
687                                         if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
688                                                 return XNamespace.Get (a.Value);
689                         return XNamespace.None; // nothing is declared.
690                 }
691
692                 public string GetPrefixOfNamespace (XNamespace ns)
693                 {
694                         foreach (string prefix in GetPrefixOfNamespaceCore (ns))
695                                 if (GetNamespaceOfPrefix (prefix) == ns)
696                                         return prefix;
697                         return null; // nothing is declared
698                 }
699                 
700                 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
701                 {
702                         for (XElement el = this; el != null; el = el.Parent)
703                                 foreach (XAttribute a in el.Attributes ())
704                                         if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
705                                                 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
706                 }
707
708                 public void ReplaceAll (object item)
709                 {
710                         RemoveNodes ();
711                         Add (item);
712                 }
713
714                 public void ReplaceAll (params object [] items)
715                 {
716                         RemoveNodes ();
717                         Add (items);
718                 }
719
720                 public void ReplaceAttributes (object item)
721                 {
722                         RemoveAttributes ();
723                         Add (item);
724                 }
725
726                 public void ReplaceAttributes (params object [] items)
727                 {
728                         RemoveAttributes ();
729                         Add (items);
730                 }
731
732                 public void SetElementValue (XName name, object value)
733                 {
734                         XElement el = new XElement (name, value);
735                         RemoveNodes ();
736                         Add (el);
737                 }
738
739                 public void SetValue (object value)
740                 {
741                         if (value == null)
742                                 throw new ArgumentNullException ("value");
743                         if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
744                                 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
745                         RemoveNodes ();
746                         foreach (object o in XUtil.ExpandArray (value))
747                                 Add (o);
748                 }
749
750                 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
751                 {
752                         if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
753                                 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
754
755                         XAttribute a = o as XAttribute;
756                         if (a != null) {
757                                 foreach (XAttribute ia in Attributes ())
758                                         if (a.Name == ia.Name)
759                                                 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
760                                 SetAttributeObject (a);
761                                 return true;
762                         }
763                         else if (o is string && refNode is XText) {
764                                 ((XText) refNode).Value += o as string;
765                                 return true;
766                         }
767                         else
768                                 return false;
769                 }
770
771                 void IXmlSerializable.WriteXml (XmlWriter writer)
772                 {
773                         Save (writer);
774                 }
775
776                 void IXmlSerializable.ReadXml (XmlReader reader)
777                 {
778                         ReadContentFrom (reader, LoadOptions.None);
779                 }
780
781                 XmlSchema IXmlSerializable.GetSchema ()
782                 {
783                         return null;
784                 }
785         }
786 }