New test.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextWriter2.cs
index c4ae87431ff81d29aec0f104e712fc0076cd8f40..a47d0edc80b5fb13c25696cb5891d83240d5f2d2 100644 (file)
@@ -229,6 +229,7 @@ namespace Mono.Xml
                XmlNodeInfo [] elements = new XmlNodeInfo [10];
                Stack new_local_namespaces = new Stack ();
                ArrayList explicit_nsdecls = new ArrayList ();
+               NamespaceHandling namespace_handling;
 
                bool indent;
                int indent_count = 2;
@@ -239,6 +240,8 @@ namespace Mono.Xml
 
                char quote_char = '"';
 
+               bool v2;
+
                // Constructors
 
                public XmlTextWriter (string filename, Encoding encoding)
@@ -257,20 +260,25 @@ namespace Mono.Xml
 
                public XmlTextWriter (TextWriter writer)
                {
+                       if (writer == null)
+                               throw new ArgumentNullException ("writer");
+                       ignore_encoding = (writer.Encoding == null);
                        Initialize (writer);
                        allow_doc_fragment = true;
                }
 
 #if NET_2_0
                internal XmlTextWriter (
-                       TextWriter writer, XmlWriterSettings settings)
+                       TextWriter writer, XmlWriterSettings settings, bool closeOutput)
                {
+                       v2 = true;
+
                        if (settings == null)
                                settings = new XmlWriterSettings ();
 
                        Initialize (writer);
 
-                       close_output_stream = settings.CloseOutput;
+                       close_output_stream = closeOutput;
                        allow_doc_fragment =
                                settings.ConformanceLevel != ConformanceLevel.Document;
                        switch (settings.ConformanceLevel) {
@@ -301,6 +309,7 @@ namespace Mono.Xml
 
                        check_character_validity = settings.CheckCharacters;
                        newline_handling = settings.NewLineHandling;
+                       namespace_handling = settings.NamespaceHandling;
                }
 #endif
 
@@ -420,10 +429,16 @@ namespace Mono.Xml
 
                public override void Close ()
                {
-                       if (state == WriteState.Attribute)
-                               WriteEndAttribute ();
-                       while (open_count > 0)
-                               WriteEndElement ();
+#if NET_2_0
+                       if (state != WriteState.Error) {
+#endif
+                               if (state == WriteState.Attribute)
+                                       WriteEndAttribute ();
+                               while (open_count > 0)
+                                       WriteEndElement ();
+#if NET_2_0
+                       }
+#endif
 
                        if (close_output_stream)
                                writer.Close ();
@@ -466,8 +481,6 @@ namespace Mono.Xml
                        if (state != WriteState.Start)
                                throw StateError ("XmlDeclaration");
 
-                       state = WriteState.Prolog;
-
                        switch (xmldecl_state) {
                        case XmlDeclState.Ignore:
                                return;
@@ -475,6 +488,8 @@ namespace Mono.Xml
                                throw InvalidOperation ("WriteStartDocument cannot be called when ConformanceLevel is Fragment.");
                        }
 
+                       state = WriteState.Prolog;
+
                        writer.Write ("<?xml version=");
                        writer.Write (quote_char);
                        writer.Write ("1.0");
@@ -599,8 +614,14 @@ namespace Mono.Xml
                        if (!namespaces && prefix.Length > 0)
                                throw ArgumentError ("Namespace prefix is disabled in this XmlTextWriter.");
 
-                       if (prefix.Length > 0 && namespaceUri == null)
-                               throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+                       // If namespace URI is empty, then either prefix
+                       // must be empty as well, or there is an
+                       // existing namespace mapping for the prefix.
+                       if (prefix.Length > 0 && namespaceUri == null) {
+                               namespaceUri = nsmanager.LookupNamespace (prefix, false);
+                               if (namespaceUri == null || namespaceUri.Length == 0)
+                                       throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+                       }
                        // Considering the fact that WriteStartAttribute()
                        // automatically changes argument namespaceURI, this
                        // is kind of silly implementation. See bug #77094.
@@ -750,7 +771,7 @@ namespace Mono.Xml
                        if (open_count == 0)
                                throw InvalidOperation ("There is no more open element.");
 
-                       bool isEmpty = state != WriteState.Content;
+                       // bool isEmpty = state != WriteState.Content;
 
                        CloseStartElementCore ();
 
@@ -788,6 +809,14 @@ namespace Mono.Xml
                public override void WriteStartAttribute (
                        string prefix, string localName, string namespaceUri)
                {
+                       // LAMESPEC: this violates the expected behavior of
+                       // this method, as it incorrectly allows unbalanced
+                       // output of attributes. Microfot changes description
+                       // on its behavior at their will, regardless of
+                       // ECMA description.
+                       if (state == WriteState.Attribute)
+                               WriteEndAttribute ();
+
                        if (state != WriteState.Element && state != WriteState.Start)
                                throw StateError ("Attribute");
 
@@ -812,7 +841,7 @@ namespace Mono.Xml
                                if (prefix == "xml")
                                        namespaceUri = XmlNamespace;
                                // infer namespace URI.
-                               else if ((object) namespaceUri == null) {
+                               else if ((object) namespaceUri == null || (v2 && namespaceUri.Length == 0)) {
                                        if (isNSDecl)
                                                namespaceUri = XmlnsNamespace;
                                        else
@@ -827,10 +856,14 @@ namespace Mono.Xml
                                if (isNSDecl && namespaceUri != XmlnsNamespace)
                                        throw ArgumentError (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
 
-                               // If namespace URI is empty, then prefix
-                               // must be empty as well.
-                               if (prefix.Length > 0 && namespaceUri.Length == 0)
-                                       throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+                               // If namespace URI is empty, then either prefix
+                               // must be empty as well, or there is an
+                               // existing namespace mapping for the prefix.
+                               if (prefix.Length > 0 && namespaceUri.Length == 0) {
+                                       namespaceUri = nsmanager.LookupNamespace (prefix, false);
+                                       if (namespaceUri == null || namespaceUri.Length == 0)
+                                               throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
+                               }
 
                                // Dive into extremely complex procedure.
                                if (!isNSDecl && namespaceUri.Length > 0)
@@ -839,7 +872,7 @@ namespace Mono.Xml
                        }
 
                        if (indent_attributes)
-                               WriteIndent ();
+                               WriteIndentAttribute ();
                        else if (state != WriteState.Start)
                                writer.Write (' ');
 
@@ -938,13 +971,24 @@ namespace Mono.Xml
                                            value.Length == 0)
                                                throw ArgumentError ("Non-empty prefix must be mapped to non-empty namespace URI.");
                                        string existing = nsmanager.LookupNamespace (preserved_name, false);
-                                       explicit_nsdecls.Add (preserved_name);
-                                       if (open_count > 0 &&
-                                           elements [open_count - 1].NS == String.Empty &&
-                                           elements [open_count - 1].Prefix == preserved_name)
-                                               ; // do nothing
-                                       else if (existing != value)
-                                               nsmanager.AddNamespace (preserved_name, value);
+
+                                       // consider OmitDuplicates here.
+                                       if ((namespace_handling & NamespaceHandling.OmitDuplicates) == 0 || existing != value)
+                                               explicit_nsdecls.Add (preserved_name);
+
+                                       if (open_count > 0) {
+
+                                               if (v2 &&
+                                                   elements [open_count - 1].Prefix == preserved_name &&
+                                                   elements [open_count - 1].NS != value)
+                                                       throw new XmlException (String.Format ("Cannot redefine the namespace for prefix '{0}' used at current element", preserved_name));
+
+                                               if (elements [open_count - 1].NS == String.Empty &&
+                                                   elements [open_count - 1].Prefix == preserved_name)
+                                                       ; // do nothing
+                                               else if (existing != value)
+                                                       nsmanager.AddNamespace (preserved_name, value);
+                                       }
                                } else {
                                        switch (preserved_name) {
                                        case "lang":
@@ -1058,7 +1102,7 @@ namespace Mono.Xml
 
                public override void WriteString (string text)
                {
-                       if (text == null || text.Length == 0)
+                       if (text == null || (text.Length == 0 && !v2))
                                return; // do nothing, including state transition.
                        ShiftStateContent ("Text", true);
 
@@ -1155,9 +1199,7 @@ namespace Mono.Xml
 
                        ShiftStateContent ("QName", true);
 
-                       string prefix =
-                               state == WriteState.Content || ns.Length > 0 ?
-                               LookupPrefix (ns) : String.Empty;
+                       string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
                        if (prefix == null) {
                                if (state == WriteState.Attribute)
                                        prefix = MockupPrefix (ns, false);
@@ -1223,26 +1265,33 @@ namespace Mono.Xml
 
                void WriteIndent ()
                {
-                       WriteIndentCore (0);
+                       WriteIndentCore (0, false);
                }
 
                void WriteIndentEndElement ()
                {
-                       WriteIndentCore (-1);
+                       WriteIndentCore (-1, false);
                }
 
-               void WriteIndentCore (int nestFix)
+               void WriteIndentAttribute ()
+               {
+                       if (!WriteIndentCore (0, true))
+                               writer.Write (' '); // space is required instead.
+               }
+
+               bool WriteIndentCore (int nestFix, bool attribute)
                {
                        if (!indent)
-                               return;
+                               return false;
                        for (int i = open_count - 1; i >= 0; i--)
-                               if (elements [i].HasSimple)
-                                       return;
+                               if (!attribute && elements [i].HasSimple)
+                                       return false;
 
                        if (state != WriteState.Start)
                                writer.Write (newline);
                        for (int i = 0; i < open_count + nestFix; i++)
                                writer.Write (indent_string);
+                       return true;
                }
 
                void OutputAutoStartDocument ()