Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XElement.cs
index 2a0b7d2d31efd050638aed956b865d2fdc6080ca..524324e47f1a341f4403bca6fafda1ebf48c3ea3 100644 (file)
@@ -28,6 +28,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Text;
 using System.Xml;
 using System.Xml.Schema;
@@ -47,64 +48,104 @@ namespace System.Xml.Linq
 
                XName name;
                XAttribute attr_first, attr_last;
-               bool explicit_is_empty;
+               bool explicit_is_empty = true;
 
-               public XElement (XName name, object value)
+               public XElement (XName name, object content)
                {
-                       SetElementValue (name, value);
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
+                       this.name = name;
+                       Add (content);
                }
 
-               public XElement (XElement source)
+               public XElement (XElement other)
                {
-                       name = source.name;
-                       Add (source.Nodes ());
+                       if (other == null)
+                               throw new ArgumentNullException ("other");
+                       name = other.name;
+                       Add (other.Attributes ());
+                       Add (other.Nodes ());
                }
 
                public XElement (XName name)
                {
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
                        this.name = name;
                }
 
-               public XElement (XName name, params object [] contents)
+               public XElement (XName name, params object [] content)
                {
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
                        this.name = name;
-                       Add (contents);
+                       Add (content);
                }
 
-               [MonoTODO ("finish")]           
-               public XElement (XStreamingElement source)
+               public XElement (XStreamingElement other)
                {
-                       this.name = source.Name;
+                       if (other == null)
+                               throw new ArgumentNullException ("other");
+                       this.name = other.Name;
+                       Add (other.Contents);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator bool (XElement element)
                {
                        if (element == null)
                                throw new ArgumentNullException ("element");
-                       return XmlConvert.ToBoolean (element.Value);
+                       return XUtil.ConvertToBoolean (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator bool? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
-                       return element.Value == null ? (bool?) null : XmlConvert.ToBoolean (element.Value);
+                               return null;
+                       
+                       return element.Value == null ? (bool?) null : XUtil.ConvertToBoolean (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator DateTime (XElement element)
                {
                        if (element == null)
                                throw new ArgumentNullException ("element");
-                       return XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
+                       return XUtil.ToDateTime (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator DateTime? (XElement element)
+               {
+                       if (element == null)
+                               return null;
+                       
+                       return element.Value == null ? (DateTime?) null : XUtil.ToDateTime (element.Value);
+               }
+
+#if !TARGET_JVM // Same as for System.Xml.XmlConvert.ToDateTimeOffset
+
+               [CLSCompliant (false)]
+               public static explicit operator DateTimeOffset (XElement element)
                {
                        if (element == null)
                                throw new ArgumentNullException ("element");
-                       return element.Value == null ? (DateTime?) null : XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
+                       return XmlConvert.ToDateTimeOffset (element.Value);
                }
 
+               [CLSCompliant (false)]
+               public static explicit operator DateTimeOffset? (XElement element)
+               {
+                       if (element == null)
+                               return null;
+                       
+                       return element.Value == null ? (DateTimeOffset?) null : XmlConvert.ToDateTimeOffset (element.Value);
+               }
+
+#endif
+
+               [CLSCompliant (false)]
                public static explicit operator decimal (XElement element)
                {
                        if (element == null)
@@ -112,13 +153,16 @@ namespace System.Xml.Linq
                        return XmlConvert.ToDecimal (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator decimal? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator double (XElement element)
                {
                        if (element == null)
@@ -126,13 +170,16 @@ namespace System.Xml.Linq
                        return XmlConvert.ToDouble (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator double? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator float (XElement element)
                {
                        if (element == null)
@@ -140,13 +187,16 @@ namespace System.Xml.Linq
                        return XmlConvert.ToSingle (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator float? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator Guid (XElement element)
                {
                        if (element == null)
@@ -154,13 +204,16 @@ namespace System.Xml.Linq
                        return XmlConvert.ToGuid (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator Guid? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator int (XElement element)
                {
                        if (element == null)
@@ -168,13 +221,16 @@ namespace System.Xml.Linq
                        return XmlConvert.ToInt32 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator int? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator long (XElement element)
                {
                        if (element == null)
@@ -182,10 +238,12 @@ namespace System.Xml.Linq
                        return XmlConvert.ToInt64 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator long? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
                }
 
@@ -201,7 +259,8 @@ namespace System.Xml.Linq
                public static explicit operator uint? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
                }
 
@@ -217,10 +276,12 @@ namespace System.Xml.Linq
                public static explicit operator ulong? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator TimeSpan (XElement element)
                {
                        if (element == null)
@@ -228,17 +289,21 @@ namespace System.Xml.Linq
                        return XmlConvert.ToTimeSpan (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator TimeSpan? (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator string (XElement element)
                {
                        if (element == null)
-                               throw new ArgumentNullException ("element");
+                               return null;
+                       
                        return element.Value;
                }
 
@@ -273,9 +338,11 @@ namespace System.Xml.Linq
                public XName Name {
                        get { return name; }
                        set {
-                               if (name == null)
-                                       throw new ArgumentNullException ("value");
+                               if (value == null)
+                                       throw new ArgumentNullException ("Name");
+                               OnNameChanging (this);
                                name = value;
+                               OnNameChanged (this);
                        }
                }
 
@@ -286,10 +353,13 @@ namespace System.Xml.Linq
                public string Value {
                        get {
                                StringBuilder sb = null;
-                               foreach (object s in Nodes ()) {
+                               foreach (XNode n in Nodes ()) {
                                        if (sb == null)
                                                sb = new StringBuilder ();
-                                       sb.Append (s);
+                                       if (n is XText)
+                                               sb.Append (((XText) n).Value);
+                                       else if (n is XElement)
+                                               sb.Append (((XElement) n).Value);
                                }
                                return sb == null ? String.Empty : sb.ToString ();
                        }
@@ -335,6 +405,25 @@ namespace System.Xml.Linq
                                        yield return a;
                }
 
+               static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
+               {
+#if MOONLIGHT
+                       // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
+                       settings.DtdProcessing = DtdProcessing.Parse;
+#else
+                       settings.ProhibitDtd = false;
+#endif
+
+                       settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
+               }
+
+               static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
+               {
+                       var settings = new XmlReaderSettings ();
+                       DefineDefaultSettings (settings, options);
+                       return settings;
+               }
+
                public static XElement Load (string uri)
                {
                        return Load (uri, LoadOptions.None);
@@ -342,24 +431,24 @@ namespace System.Xml.Linq
 
                public static XElement Load (string uri, LoadOptions options)
                {
-                       XmlReaderSettings s = new XmlReaderSettings ();
-                       s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
+                       XmlReaderSettings s = CreateDefaultSettings (options);
+
                        using (XmlReader r = XmlReader.Create (uri, s)) {
-                               return LoadCore (r);
+                               return LoadCore (r, options);
                        }
                }
 
-               public static XElement Load (TextReader tr)
+               public static XElement Load (TextReader textReader)
                {
-                       return Load (tr, LoadOptions.None);
+                       return Load (textReader, LoadOptions.None);
                }
 
-               public static XElement Load (TextReader tr, LoadOptions options)
+               public static XElement Load (TextReader textReader, LoadOptions options)
                {
-                       XmlReaderSettings s = new XmlReaderSettings ();
-                       s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
-                       using (XmlReader r = XmlReader.Create (tr, s)) {
-                               return LoadCore (r);
+                       XmlReaderSettings s = CreateDefaultSettings (options);
+
+                       using (XmlReader r = XmlReader.Create (textReader, s)) {
+                               return LoadCore (r, options);
                        }
                }
 
@@ -370,34 +459,56 @@ namespace System.Xml.Linq
 
                public static XElement Load (XmlReader reader, LoadOptions options)
                {
-                       XmlReaderSettings s = reader.Settings.Clone ();
-                       s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
+                       XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
+                       DefineDefaultSettings (s, options);
+
                        using (XmlReader r = XmlReader.Create (reader, s)) {
-                               return LoadCore (r);
+                               return LoadCore (r, options);
                        }
                }
 
-               static XElement LoadCore (XmlReader r)
+#if MOONLIGHT || MOBILE || NET_4_0
+               public static XElement Load (Stream stream)
+               {
+                       return Load (stream, LoadOptions.None);
+               }
+
+               public static XElement Load (Stream stream, LoadOptions options)
+               {
+                       XmlReaderSettings s = new XmlReaderSettings ();
+                       DefineDefaultSettings (s, options);
+
+                       using (XmlReader r = XmlReader.Create (stream, s)) {
+                               return LoadCore (r, options);
+                       }
+               }
+#endif
+
+               internal static XElement LoadCore (XmlReader r, LoadOptions options)
                {
                        r.MoveToContent ();
                        if (r.NodeType != XmlNodeType.Element)
                                throw new InvalidOperationException ("The XmlReader must be positioned at an element");
                        XName name = XName.Get (r.LocalName, r.NamespaceURI);
                        XElement e = new XElement (name);
+                       e.FillLineInfoAndBaseUri (r, options);
+
                        if (r.MoveToFirstAttribute ()) {
                                do {
                                        // not sure how current Orcas behavior makes sense here though ...
                                        if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
-                                               e.SetAttributeValue (XNamespace.Blank.GetName ("xmlns"), r.Value);
+                                               e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
                                        else
                                                e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
+                                       e.LastAttribute.FillLineInfoAndBaseUri (r, options);
                                } while (r.MoveToNextAttribute ());
                                r.MoveToElement ();
                        }
                        if (!r.IsEmptyElement) {
                                r.Read ();
-                               e.ReadContentFrom (r);
+                               e.ReadContentFrom (r, options);
                                r.ReadEndElement ();
+                               e.explicit_is_empty = false;
                        } else {
                                e.explicit_is_empty = true;
                                r.Read ();
@@ -405,14 +516,14 @@ namespace System.Xml.Linq
                        return e;
                }
 
-               public static XElement Parse (string s)
+               public static XElement Parse (string text)
                {
-                       return Parse (s, LoadOptions.None);
+                       return Parse (text, LoadOptions.None);
                }
 
-               public static XElement Parse (string s, LoadOptions options)
+               public static XElement Parse (string text, LoadOptions options)
                {
-                       return Load (new StringReader (s), options);
+                       return Load (new StringReader (text), options);
                }
 
                public void RemoveAll ()
@@ -427,49 +538,70 @@ namespace System.Xml.Linq
                                attr_last.Remove ();
                }
 
-               public void Save (string filename)
+               public void Save (string fileName)
                {
-                       Save (filename, SaveOptions.None);
+                       Save (fileName, SaveOptions.None);
                }
 
-               public void Save (string filename, SaveOptions options)
+               public void Save (string fileName, SaveOptions options)
                {
                        XmlWriterSettings s = new XmlWriterSettings ();
-                       if ((options & SaveOptions.DisableFormatting) != 0) {
-                               // hacky!
+
+                       if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
                                s.Indent = true;
-                               s.IndentChars = String.Empty;
-                               s.NewLineChars = String.Empty;
-                       }
-                       using (XmlWriter w = XmlWriter.Create (filename)) {
+#if NET_4_0 || MOONLIGHT || MOBILE
+                       if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
+                               s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
+#endif
+                       using (XmlWriter w = XmlWriter.Create (fileName, s)) {
                                Save (w);
                        }
                }
 
-               public void Save (TextWriter tw)
+               public void Save (TextWriter textWriter)
                {
-                       Save (tw, SaveOptions.None);
+                       Save (textWriter, SaveOptions.None);
                }
 
-               public void Save (TextWriter tw, SaveOptions options)
+               public void Save (TextWriter textWriter, SaveOptions options)
                {
                        XmlWriterSettings s = new XmlWriterSettings ();
-                       if ((options & SaveOptions.DisableFormatting) != 0) {
-                               // hacky!
+                       
+                       if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
                                s.Indent = true;
-                               s.IndentChars = String.Empty;
-                               s.NewLineChars = String.Empty;
-                       }
-                       using (XmlWriter w = XmlWriter.Create (tw)) {
+#if NET_4_0 || MOONLIGHT || MOBILE
+                       if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
+                               s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
+#endif
+                       using (XmlWriter w = XmlWriter.Create (textWriter, s)) {
                                Save (w);
                        }
                }
 
-               public void Save (XmlWriter w)
+               public void Save (XmlWriter writer)
+               {
+                       WriteTo (writer);
+               }
+
+#if NET_4_0 || MOONLIGHT || MOBILE
+               public void Save (Stream stream)
                {
-                       WriteTo (w);
+                       Save (stream, SaveOptions.None);
                }
 
+               public void Save (Stream stream, SaveOptions options)
+               {
+                       XmlWriterSettings s = new XmlWriterSettings ();
+                       if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
+                               s.Indent = true;
+                       if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
+                               s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
+
+                       using (var writer = XmlWriter.Create (stream, s)){
+                               Save (writer);
+                       }
+               }
+#endif
                public IEnumerable <XElement> AncestorsAndSelf ()
                {
                        return GetAncestorList (null, true);
@@ -512,62 +644,107 @@ namespace System.Xml.Linq
                                        a.Remove ();
                        } else {
                                if (a == null) {
-                                       a = new XAttribute (name, value);
-                                       a.SetOwner (this);
-                                       if (attr_first == null) {
-                                               attr_first = a;
-                                               attr_last = a;
-                                       } else {
-                                               attr_last.NextAttribute = a;
-                                               a.PreviousAttribute = attr_last;
-                                               attr_last = a;
-                                       }
+                                       SetAttributeObject (new XAttribute (name, value));
                                }
                                else
                                        a.Value = XUtil.ToString (value);
                        }
                }
 
-               public override void WriteTo (XmlWriter w)
+               void SetAttributeObject (XAttribute a)
                {
-                       w.WriteStartElement (name.LocalName, name.Namespace.NamespaceName);
+                       OnAddingObject (a);
+                       a = (XAttribute) XUtil.GetDetachedObject (a);
+                       a.SetOwner (this);
+                       if (attr_first == null) {
+                               attr_first = a;
+                               attr_last = a;
+                       } else {
+                               attr_last.NextAttribute = a;
+                               a.PreviousAttribute = attr_last;
+                               attr_last = a;
+                       }
+                       OnAddedObject (a);
+               }
+
+               string LookupPrefix (string ns, XmlWriter w)
+               {
+                       string prefix = ns.Length > 0 ? GetPrefixOfNamespace (ns) ?? w.LookupPrefix (ns) : String.Empty;
+                       foreach (XAttribute a in Attributes ()) {
+                               if (a.IsNamespaceDeclaration && a.Value == ns) {
+                                       if (a.Name.Namespace == XNamespace.Xmlns)
+                                               prefix = a.Name.LocalName;
+                                       // otherwise xmlns="..."
+                                       break;
+                               }
+                       }
+                       return prefix;
+               }
+               
+               static string CreateDummyNamespace (ref int createdNS, IEnumerable<XAttribute> atts, bool isAttr)
+               {
+                       if (!isAttr && atts.All (a => a.Name.LocalName != "xmlns" || a.Name.NamespaceName == XNamespace.Xmlns.NamespaceName))
+                               return String.Empty;
+                       string p = null;
+                       do {
+                               p = "p" + (++createdNS);
+                               // check conflict
+                               if (atts.All (a => a.Name.LocalName != p || a.Name.NamespaceName == XNamespace.Xmlns.NamespaceName))
+                                       break;
+                       } while (true);
+                       return p;
+               }
+
+               public override void WriteTo (XmlWriter writer)
+               {
+                       // some people expect the same prefix output as in input,
+                       // in the loss of performance... see bug #466423.
+                       string prefix = LookupPrefix (name.NamespaceName, writer);
+                       int createdNS = 0;
+                       if (prefix == null)
+                               prefix = CreateDummyNamespace (ref createdNS, Attributes (), false);
+
+                       writer.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
 
                        foreach (XAttribute a in Attributes ()) {
                                if (a.IsNamespaceDeclaration) {
                                        if (a.Name.Namespace == XNamespace.Xmlns)
-                                               w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
+                                               writer.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
                                        else
-                                               w.WriteAttributeString ("xmlns", a.Value);
+                                               writer.WriteAttributeString ("xmlns", a.Value);
+                               } else {
+                                       string apfix = LookupPrefix (a.Name.NamespaceName, writer);
+                                       if (apfix == null)
+                                               apfix = CreateDummyNamespace (ref createdNS, Attributes (), true);
+                                       writer.WriteAttributeString (apfix, a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
                                }
-                               else
-                                       w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
                        }
 
                        foreach (XNode node in Nodes ())
-                               node.WriteTo (w);
+                               node.WriteTo (writer);
 
                        if (explicit_is_empty)
-                               w.WriteEndElement ();
+                               writer.WriteEndElement ();
                        else
-                               w.WriteFullEndElement ();
+                               writer.WriteFullEndElement ();
                }
 
                public XNamespace GetDefaultNamespace ()
                {
                        for (XElement el = this; el != null; el = el.Parent)
                                foreach (XAttribute a in el.Attributes ())
-                                       if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.Blank)
+                                       if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
                                                return XNamespace.Get (a.Value);
-                       return XNamespace.Blank; // nothing is declared.
+                       return XNamespace.None; // nothing is declared.
                }
 
                public XNamespace GetNamespaceOfPrefix (string prefix)
                {
                        for (XElement el = this; el != null; el = el.Parent)
                                foreach (XAttribute a in el.Attributes ())
-                                       if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix)
+                                       if (a.IsNamespaceDeclaration && (prefix.Length == 0 && a.Name.LocalName == "xmlns" || a.Name.LocalName == prefix))
                                                return XNamespace.Get (a.Value);
-                       return XNamespace.Blank; // nothing is declared.
+                       return XNamespace.None; // nothing is declared.
                }
 
                public string GetPrefixOfNamespace (XNamespace ns)
@@ -583,51 +760,74 @@ namespace System.Xml.Linq
                        for (XElement el = this; el != null; el = el.Parent)
                                foreach (XAttribute a in el.Attributes ())
                                        if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
-                                               yield return a.Name.Namespace == XNamespace.Blank ? String.Empty : a.Name.LocalName;
+                                               yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
                }
 
-               public void ReplaceAll (object item)
+               public void ReplaceAll (object content)
                {
                        RemoveNodes ();
-                       Add (item);
+                       Add (content);
                }
 
-               public void ReplaceAll (params object [] items)
+               public void ReplaceAll (params object [] content)
                {
                        RemoveNodes ();
-                       Add (items);
+                       Add (content);
                }
 
-               public void ReplaceAttributes (object item)
+               public void ReplaceAttributes (object content)
                {
                        RemoveAttributes ();
-                       Add (item);
+                       Add (content);
                }
 
-               public void ReplaceAttributes (params object [] items)
+               public void ReplaceAttributes (params object [] content)
                {
                        RemoveAttributes ();
-                       Add (items);
+                       Add (content);
                }
 
                public void SetElementValue (XName name, object value)
                {
-                       XElement el = new XElement (name, value);
-                       RemoveNodes ();
-                       Add (el);
+                       var element = Element (name);
+                       if (element == null && value != null) {
+                               Add (new XElement (name, value));
+                       } else if (element != null && value == null) {
+                               element.Remove ();
+                       } else
+                               element.SetValue (value);
                }
 
                public void SetValue (object value)
                {
-                       XNode n = XUtil.ToNode (value);
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+                       if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
+                               throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
                        RemoveNodes ();
-                       Add (n);
+                       foreach (object o in XUtil.ExpandArray (value))
+                               Add (o);
                }
 
-               internal override void OnAdded (XNode node, bool addFirst)
+               internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
                {
-                       if (node is XDocument || node is XDocumentType)
-                               throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", node.GetType ()));
+                       if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
+                               throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
+
+                       XAttribute a = o as XAttribute;
+                       if (a != null) {
+                               foreach (XAttribute ia in Attributes ())
+                                       if (a.Name == ia.Name)
+                                               throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
+                               SetAttributeObject (a);
+                               return true;
+                       }
+                       else if (o is string && refNode is XText) {
+                               ((XText) refNode).Value += o as string;
+                               return true;
+                       }
+                       else
+                               return false;
                }
 
                void IXmlSerializable.WriteXml (XmlWriter writer)
@@ -637,7 +837,7 @@ namespace System.Xml.Linq
 
                void IXmlSerializable.ReadXml (XmlReader reader)
                {
-                       ReadContentFrom (reader);
+                       ReadContentFrom (reader, LoadOptions.None);
                }
 
                XmlSchema IXmlSerializable.GetSchema ()