Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XElement.cs
index ef6163bc3a5ef789b6d4a3168034381786e7357f..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;
@@ -49,66 +50,102 @@ namespace System.Xml.Linq
                XAttribute attr_first, attr_last;
                bool explicit_is_empty = true;
 
-               public XElement (XName name, object value)
+               public XElement (XName name, object content)
                {
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
                        this.name = name;
-                       Add (value);
+                       Add (content);
                }
 
-               public XElement (XElement source)
+               public XElement (XElement other)
                {
-                       name = source.name;
-                       Add (source.Attributes ());
-                       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);
                }
 
-               public XElement (XStreamingElement source)
+               public XElement (XStreamingElement other)
                {
-                       this.name = source.Name;
-                       Add (source.Contents);
+                       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)
                                return null;
                        
-                       return element.Value == null ? (bool?) null : XmlConvert.ToBoolean (element.Value);
+                       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 : XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
+                       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 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)
@@ -116,6 +153,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToDecimal (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator decimal? (XElement element)
                {
                        if (element == null)
@@ -124,6 +162,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator double (XElement element)
                {
                        if (element == null)
@@ -131,6 +170,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToDouble (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator double? (XElement element)
                {
                        if (element == null)
@@ -139,6 +179,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator float (XElement element)
                {
                        if (element == null)
@@ -146,6 +187,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToSingle (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator float? (XElement element)
                {
                        if (element == null)
@@ -154,6 +196,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator Guid (XElement element)
                {
                        if (element == null)
@@ -161,6 +204,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToGuid (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator Guid? (XElement element)
                {
                        if (element == null)
@@ -169,6 +213,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator int (XElement element)
                {
                        if (element == null)
@@ -176,6 +221,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToInt32 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator int? (XElement element)
                {
                        if (element == null)
@@ -184,6 +230,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator long (XElement element)
                {
                        if (element == null)
@@ -191,6 +238,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToInt64 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator long? (XElement element)
                {
                        if (element == null)
@@ -233,6 +281,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator TimeSpan (XElement element)
                {
                        if (element == null)
@@ -240,6 +289,7 @@ namespace System.Xml.Linq
                        return XmlConvert.ToTimeSpan (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator TimeSpan? (XElement element)
                {
                        if (element == null)
@@ -248,6 +298,7 @@ namespace System.Xml.Linq
                        return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
                }
 
+               [CLSCompliant (false)]
                public static explicit operator string (XElement element)
                {
                        if (element == null)
@@ -287,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);
                        }
                }
 
@@ -354,7 +407,7 @@ namespace System.Xml.Linq
 
                static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
                {
-#if NET_2_1
+#if MOONLIGHT
                        // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
                        settings.DtdProcessing = DtdProcessing.Parse;
 #else
@@ -385,16 +438,16 @@ namespace System.Xml.Linq
                        }
                }
 
-               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 = CreateDefaultSettings (options);
 
-                       using (XmlReader r = XmlReader.Create (tr, s)) {
+                       using (XmlReader r = XmlReader.Create (textReader, s)) {
                                return LoadCore (r, options);
                        }
                }
@@ -414,6 +467,23 @@ namespace System.Xml.Linq
                        }
                }
 
+#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 ();
@@ -446,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 ()
@@ -468,39 +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 ();
-                       s.Indent = options != SaveOptions.DisableFormatting;
-                       using (XmlWriter w = XmlWriter.Create (filename, s)) {
+
+                       if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
+                               s.Indent = true;
+#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 ();
-                       s.Indent = options != SaveOptions.DisableFormatting;
-                       using (XmlWriter w = XmlWriter.Create (tw, s)) {
+                       
+                       if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
+                               s.Indent = true;
+#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 (w);
+                       WriteTo (writer);
                }
 
+#if NET_4_0 || MOONLIGHT || MOBILE
+               public void Save (Stream stream)
+               {
+                       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);
@@ -552,6 +653,7 @@ namespace System.Xml.Linq
 
                void SetAttributeObject (XAttribute a)
                {
+                       OnAddingObject (a);
                        a = (XAttribute) XUtil.GetDetachedObject (a);
                        a.SetOwner (this);
                        if (attr_first == null) {
@@ -562,42 +664,69 @@ namespace System.Xml.Linq
                                a.PreviousAttribute = attr_last;
                                attr_last = a;
                        }
+                       OnAddedObject (a);
                }
 
-               public override void WriteTo (XmlWriter w)
+               string LookupPrefix (string ns, XmlWriter w)
                {
-                       // some people expect the same prefix output as in input,
-                       // in the loss of performance... see bug #466423.
-                       string prefix = name.NamespaceName.Length > 0 ? w.LookupPrefix (name.Namespace.NamespaceName) : String.Empty;
+                       string prefix = ns.Length > 0 ? GetPrefixOfNamespace (ns) ?? w.LookupPrefix (ns) : String.Empty;
                        foreach (XAttribute a in Attributes ()) {
-                               if (a.IsNamespaceDeclaration && a.Value == name.Namespace.NamespaceName) {
+                               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);
 
-                       w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
+                       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 ()
@@ -613,7 +742,7 @@ namespace System.Xml.Linq
                {
                        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.None; // nothing is declared.
                }
@@ -634,35 +763,39 @@ namespace System.Xml.Linq
                                                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)