New tests.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextWriter2.cs
index c539e421f6c3cd948a9a0f557cd4d144e578bcea..a47d0edc80b5fb13c25696cb5891d83240d5f2d2 100644 (file)
@@ -195,6 +195,13 @@ namespace Mono.Xml
                        }
                }
 
+               enum XmlDeclState {
+                       Allow,
+                       Ignore,
+                       Auto,
+                       Prohibit,
+               }
+
                // Instance fields
 
                Stream base_stream;
@@ -209,11 +216,12 @@ namespace Mono.Xml
                bool close_output_stream = true;
                bool ignore_encoding;
                bool namespaces = true;
-               bool output_xmldecl = false;
+               XmlDeclState xmldecl_state = XmlDeclState.Allow;
 
                bool check_character_validity;
                NewLineHandling newline_handling = NewLineHandling.None;
 
+               bool is_document_entity;
                WriteState state = WriteState.Start;
                XmlNodeType node_state = XmlNodeType.None;
                XmlNamespaceManager nsmanager;
@@ -221,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;
@@ -231,6 +240,8 @@ namespace Mono.Xml
 
                char quote_char = '"';
 
+               bool v2;
+
                // Constructors
 
                public XmlTextWriter (string filename, Encoding encoding)
@@ -249,22 +260,47 @@ 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
-               XmlTextWriter (
-                       TextWriter writer, XmlWriterSettings settings)
+               internal XmlTextWriter (
+                       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 != System.Xml.ConformanceLevel.Document;
+                               settings.ConformanceLevel != ConformanceLevel.Document;
+                       switch (settings.ConformanceLevel) {
+                       case ConformanceLevel.Auto:
+                               xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Allow;
+                               break;
+                       case ConformanceLevel.Document:
+                               // LAMESPEC:
+                               // On MSDN, XmlWriterSettings.OmitXmlDeclaration is documented as:
+                               // "The XML declaration is always written if
+                               //  ConformanceLevel is set to Document, even 
+                               //  if OmitXmlDeclaration is set to true. "
+                               // but it is incorrect. It does consider 
+                               // OmitXmlDeclaration property.
+                               xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Auto;
+                               break;
+                       case ConformanceLevel.Fragment:
+                               xmldecl_state = XmlDeclState.Prohibit;
+                               break;
+                       }
+                       if (settings.Indent)
+                               Formatting = Formatting.Indented;
                        indent_string = settings.IndentChars == null ?
                                String.Empty : settings.IndentChars;
                        if (settings.NewLineChars != null)
@@ -273,13 +309,14 @@ namespace Mono.Xml
 
                        check_character_validity = settings.CheckCharacters;
                        newline_handling = settings.NewLineHandling;
-                       if (settings.OmitXmlDeclaration)
-                               output_xmldecl = false;
+                       namespace_handling = settings.NamespaceHandling;
                }
 #endif
 
                void Initialize (TextWriter writer)
                {
+                       if (writer == null)
+                               throw new ArgumentNullException ("writer");
                        XmlNameTable name_table = new NameTable ();
                        this.writer = writer;
                        if (writer is StreamWriter)
@@ -299,41 +336,12 @@ namespace Mono.Xml
 #if NET_2_0
                // 2.0 XmlWriterSettings support
 
-               internal bool CheckCharacters {
-                       set { check_character_validity = value; }
-               }
-
-               internal bool CloseOutput {
-                       set { close_output_stream = value; }
-               }
-
                // As for ConformanceLevel, MS.NET is inconsistent with
                // MSDN documentation. For example, even if ConformanceLevel
                // is set as .Auto, multiple WriteStartDocument() calls
                // result in an error.
                // ms-help://MS.NETFramework.v20.en/wd_xml/html/7db8802b-53d8-4735-a637-4d2d2158d643.htm
-               [MonoTODO]
-               internal ConformanceLevel ConformanceLevel {
-                       set {
-                               allow_doc_fragment = (value == System.Xml.ConformanceLevel.Fragment);
-                       }
-               }
-
-               internal string IndentChars {
-                       set { indent_string = (value == null) ? String.Empty : value; }
-               }
-
-               internal string NewLineChars {
-                       set { newline = (value == null) ? String.Empty : value; }
-               }
-
-               internal bool NewLineOnAttributes {
-                       set { indent_attributes = value; }
-               }
 
-               internal bool OmitXmlDeclaration {
-                       set { output_xmldecl = !value; }
-               }
 #endif
 
                // Literal Output Control
@@ -341,8 +349,12 @@ namespace Mono.Xml
                public Formatting Formatting {
                        get { return indent ? Formatting.Indented : Formatting.None; }
                        set {
-                               if (state != WriteState.Start)
-                                       throw InvalidOperation ("Formatting must be set before it is actually used to write output.");
+                               // Someone thinks it should be settable even
+                               // after writing some content (bug #78148).
+                               // I totally disagree but here is the fix.
+
+                               //if (state != WriteState.Start)
+                               //      throw InvalidOperation ("Formatting must be set before it is actually used to write output.");
                                indent = (value == Formatting.Indented);
                        }
                }
@@ -417,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 ();
@@ -449,11 +467,13 @@ namespace Mono.Xml
                public override void WriteStartDocument ()
                {
                        WriteStartDocumentCore (false, false);
+                       is_document_entity = true;
                }
 
                public override void WriteStartDocument (bool standalone)
                {
                        WriteStartDocumentCore (true, standalone);
+                       is_document_entity = true;
                }
 
                void WriteStartDocumentCore (bool outputStd, bool standalone)
@@ -461,6 +481,15 @@ namespace Mono.Xml
                        if (state != WriteState.Start)
                                throw StateError ("XmlDeclaration");
 
+                       switch (xmldecl_state) {
+                       case XmlDeclState.Ignore:
+                               return;
+                       case XmlDeclState.Prohibit:
+                               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");
@@ -479,8 +508,7 @@ namespace Mono.Xml
                        }
                        writer.Write ("?>");
 
-                       output_xmldecl = false;
-                       state = WriteState.Prolog;
+                       xmldecl_state = XmlDeclState.Ignore;
                }
 
                public override void WriteEndDocument ()
@@ -500,6 +528,7 @@ namespace Mono.Xml
                                WriteEndElement ();
 
                        state = WriteState.Start;
+                       is_document_entity = false;
                }
 
                // DocType Declaration
@@ -516,7 +545,7 @@ namespace Mono.Xml
                                throw StateError ("DocType");
                        node_state = XmlNodeType.DocumentType;
 
-                       if (output_xmldecl)
+                       if (xmldecl_state == XmlDeclState.Auto)
                                OutputAutoStartDocument ();
 
                        WriteIndent ();
@@ -585,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.
@@ -599,7 +634,7 @@ namespace Mono.Xml
                                throw new ArgumentException ("A prefix cannot be equivalent to \"xml\" in case-insensitive match.");
 
 
-                       if (output_xmldecl)
+                       if (xmldecl_state == XmlDeclState.Auto)
                                OutputAutoStartDocument ();
                        if (state == WriteState.Element)
                                CloseStartElement ();
@@ -646,7 +681,7 @@ namespace Mono.Xml
                        open_count++;
 
                        if (namespaces && namespaceUri != null) {
-                               string oldns = nsmanager.LookupNamespace (prefix);
+                               string oldns = nsmanager.LookupNamespace (prefix, false);
                                if (oldns != namespaceUri) {
                                        nsmanager.AddNamespace (prefix, namespaceUri);
                                        new_local_namespaces.Push (prefix);
@@ -696,7 +731,7 @@ namespace Mono.Xml
 
                        for (int i = idx; i < explicit_nsdecls.Count; i++) {
                                string prefix = (string) explicit_nsdecls [i];
-                               string ns = nsmanager.LookupNamespace (prefix);
+                               string ns = nsmanager.LookupNamespace (prefix, false);
                                if (ns == null)
                                        continue; // superceded
                                if (prefix.Length > 0) {
@@ -736,21 +771,25 @@ 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 ();
 
                        nsmanager.PopScope ();
-                       bool doIndent = !elements [open_count - 1].HasSimple;
-                       if (open_count > 1)
-                               doIndent &= !elements [open_count - 2].HasSimple;
+
+                       if (state == WriteState.Element) {
+                               if (full)
+                                       writer.Write ('>');
+                               else
+                                       writer.Write (" />");
+                       }
+
+                       if (full || state == WriteState.Content)
+                               WriteIndentEndElement ();
+
                        XmlNodeInfo info = elements [--open_count];
 
                        if (full || state == WriteState.Content) {
-                               if (state == WriteState.Element)
-                                       writer.Write ('>');
-                               if (doIndent && (full || !isEmpty))
-                                       DoWriteIndent ();
                                writer.Write ("</");
                                if (info.Prefix.Length > 0) {
                                        writer.Write (info.Prefix);
@@ -758,8 +797,6 @@ namespace Mono.Xml
                                }
                                writer.Write (info.LocalName);
                                writer.Write ('>');
-                       } else {
-                               writer.Write (" />");
                        }
 
                        state = WriteState.Content;
@@ -772,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");
 
@@ -796,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
@@ -811,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)
@@ -823,7 +872,7 @@ namespace Mono.Xml
                        }
 
                        if (indent_attributes)
-                               WriteIndent ();
+                               WriteIndentAttribute ();
                        else if (state != WriteState.Start)
                                writer.Write (' ');
 
@@ -863,19 +912,19 @@ namespace Mono.Xml
                        bool mockup = false;
                        if (prefix.Length == 0) {
                                prefix = LookupPrefix (ns);
-                               if (prefix != null)
+                               if (prefix != null && prefix.Length > 0)
                                        return prefix;
                                mockup = true;
                        } else {
                                prefix = nsmanager.NameTable.Add (prefix);
-                               string existing = nsmanager.LookupNamespace (prefix);
+                               string existing = nsmanager.LookupNamespace (prefix, true);
                                if (existing == ns)
                                        return prefix;
                                if (existing != null) {
                                        // See code comment on the head of
                                        // this source file.
                                        nsmanager.RemoveNamespace (prefix, existing);
-                                       if (nsmanager.LookupNamespace (prefix) != existing) {
+                                       if (nsmanager.LookupNamespace (prefix, true) != existing) {
                                                mockup = true;
                                                nsmanager.AddNamespace (prefix, existing);
                                        }
@@ -921,14 +970,25 @@ namespace Mono.Xml
                                        if (preserved_name.Length > 0 &&
                                            value.Length == 0)
                                                throw ArgumentError ("Non-empty prefix must be mapped to non-empty namespace URI.");
-                                       string existing = nsmanager.LookupNamespace (preserved_name);
-                                       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);
+                                       string existing = nsmanager.LookupNamespace (preserved_name, false);
+
+                                       // 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":
@@ -965,14 +1025,17 @@ namespace Mono.Xml
                        if (text == null)
                                throw ArgumentError ("text");
 
-                       WriteIndent ();
-
                        if (text.Length > 0 && text [text.Length - 1] == '-')
                                throw ArgumentError ("An input string to WriteComment method must not end with '-'. Escape it with '&#2D;'.");
                        if (StringUtil.IndexOf (text, "--") > 0)
                                throw ArgumentError ("An XML comment cannot end with \"-\".");
 
-                       ShiftStateTopLevel ("Comment", false, false);
+                       if (state == WriteState.Attribute || state == WriteState.Element)
+                               CloseStartElement ();
+
+                       WriteIndent ();
+
+                       ShiftStateTopLevel ("Comment", false, false, false);
 
                        writer.Write ("<!--");
                        writer.Write (text);
@@ -995,7 +1058,7 @@ namespace Mono.Xml
                        if (StringUtil.IndexOf (text, "?>") > 0)
                                throw ArgumentError ("Processing instruction cannot contain \"?>\" as its value.");
 
-                       ShiftStateTopLevel ("ProcessingInstruction", false, name == "xml");
+                       ShiftStateTopLevel ("ProcessingInstruction", false, name == "xml", false);
 
                        writer.Write ("<?");
                        writer.Write (name);
@@ -1019,7 +1082,7 @@ namespace Mono.Xml
                            XmlChar.IndexOfNonWhitespace (text) >= 0)
                                throw ArgumentError ("WriteWhitespace method accepts only whitespaces.");
 
-                       ShiftStateTopLevel ("Whitespace", true, false);
+                       ShiftStateTopLevel ("Whitespace", true, false, true);
 
                        writer.Write (text);
                }
@@ -1028,23 +1091,21 @@ namespace Mono.Xml
                {
                        if (text == null)
                                text = String.Empty;
-                       ShiftStateContent ("Text", false);
+                       ShiftStateContent ("CData", false);
 
                        if (StringUtil.IndexOf (text, "]]>") >= 0)
                                throw ArgumentError ("CDATA section must not contain ']]>'.");
                        writer.Write ("<![CDATA[");
-                       CheckTextValidity (text);
-                       writer.Write (text);
+                       WriteCheckedString (text);
                        writer.Write ("]]>");
                }
 
                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);
 
-                       CheckTextValidity (text);
                        WriteEscapedString (text, state == WriteState.Attribute);
                }
 
@@ -1057,9 +1118,9 @@ namespace Mono.Xml
 
                        // LAMESPEC: It rejects XMLDecl while it allows
                        // DocType which could consist of non well-formed XML.
-                       ShiftStateTopLevel ("Raw string", true, true);
+                       ShiftStateTopLevel ("Raw string", true, true, true);
 
-                       writer.Write (raw, false);
+                       writer.Write (raw);
                }
 
                public override void WriteCharEntity (char ch)
@@ -1096,7 +1157,7 @@ namespace Mono.Xml
                        if (!XmlChar.IsName (name))
                                throw ArgumentError ("Argument name must be a valid XML name.");
 
-                       ShiftStateContent ("Character", true);
+                       ShiftStateContent ("Entity reference", true);
 
                        writer.Write ('&');
                        writer.Write (name);
@@ -1138,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);
@@ -1178,7 +1237,7 @@ namespace Mono.Xml
                {
                        CheckChunkRange (buffer, index, count);
 
-                       ShiftStateContent ("Text", false);
+                       ShiftStateContent ("BinHex", true);
 
                        XmlConvert.WriteBinHex (buffer, index, count, writer);
                }
@@ -1187,7 +1246,7 @@ namespace Mono.Xml
                {
                        CheckChunkRange (buffer, index, count);
 
-                       ShiftStateContent ("Text", false);
+                       ShiftStateContent ("Chars", true);
 
                        WriteEscapedBuffer (buffer, index, count,
                                state == WriteState.Attribute);
@@ -1197,7 +1256,7 @@ namespace Mono.Xml
                {
                        CheckChunkRange (buffer, index, count);
 
-                       ShiftStateContent ("Text", false);
+                       ShiftStateContent ("Raw text", false);
 
                        writer.Write (buffer, index, count);
                }
@@ -1206,31 +1265,33 @@ namespace Mono.Xml
 
                void WriteIndent ()
                {
-                       if (!indent || (open_count > 0 &&
-                           elements [open_count - 1].HasSimple))
-                               return;
-                       if (state != WriteState.Start)
-                               writer.Write (newline);
-                       for (int i = 0; i < open_count; i++)
-                               writer.Write (indent_string);
+                       WriteIndentCore (0, false);
                }
 
-               void DoWriteIndent ()
+               void WriteIndentEndElement ()
                {
-                       if (!indent)
-                               return;
-                       writer.Write (newline);
-                       for (int i = 0; i < open_count; i++)
-                               writer.Write (indent_string);
+                       WriteIndentCore (-1, false);
                }
 
-               void CheckTextValidity (string text)
+               void WriteIndentAttribute ()
                {
-                       if (!check_character_validity)
-                               return;
-                       int idx = XmlChar.IndexOfInvalid (text, true);
-                       if (idx >= 0)
-                               throw ArgumentError (String.Format ("Invalid text character was found at string index {0}: &#x{1:X};", idx, (int) text [idx]));
+                       if (!WriteIndentCore (0, true))
+                               writer.Write (' '); // space is required instead.
+               }
+
+               bool WriteIndentCore (int nestFix, bool attribute)
+               {
+                       if (!indent)
+                               return false;
+                       for (int i = open_count - 1; i >= 0; i--)
+                               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 ()
@@ -1240,7 +1301,7 @@ namespace Mono.Xml
                        WriteStartDocumentCore (false, false);
                }
 
-               void ShiftStateTopLevel (string occured, bool allowAttribute, bool dontCheckXmlDecl)
+               void ShiftStateTopLevel (string occured, bool allowAttribute, bool dontCheckXmlDecl, bool isCharacter)
                {
                        switch (state) {
 #if NET_2_0
@@ -1249,7 +1310,9 @@ namespace Mono.Xml
                        case WriteState.Closed:
                                throw StateError (occured);
                        case WriteState.Start:
-                               if (output_xmldecl && !dontCheckXmlDecl)
+                               if (isCharacter)
+                                       CheckMixedContentState ();
+                               if (xmldecl_state == XmlDeclState.Auto && !dontCheckXmlDecl)
                                        OutputAutoStartDocument ();
                                state = WriteState.Prolog;
                                break;
@@ -1258,12 +1321,24 @@ namespace Mono.Xml
                                        break;
                                goto case WriteState.Closed;
                        case WriteState.Element:
+                               if (isCharacter)
+                                       CheckMixedContentState ();
                                CloseStartElement ();
                                break;
+                       case WriteState.Content:
+                               if (isCharacter)
+                                       CheckMixedContentState ();
+                               break;
                        }
 
-                       if (open_count > 0 &&
-                           state != WriteState.Attribute)
+               }
+
+               void CheckMixedContentState ()
+               {
+//                     if (open_count > 0 &&
+//                         state != WriteState.Attribute)
+//                             elements [open_count - 1].HasSimple = true;
+                       if (open_count > 0)
                                elements [open_count - 1].HasSimple = true;
                }
 
@@ -1277,10 +1352,11 @@ namespace Mono.Xml
                                        throw StateError (occured);
                        case WriteState.Prolog:
                        case WriteState.Start:
-                               if (!allow_doc_fragment)
+                               if (!allow_doc_fragment || is_document_entity)
                                        goto case WriteState.Closed;
-                               if (output_xmldecl)
+                               if (xmldecl_state == XmlDeclState.Auto)
                                        OutputAutoStartDocument ();
+                               CheckMixedContentState ();
                                state = WriteState.Content;
                                break;
                        case WriteState.Attribute:
@@ -1289,14 +1365,12 @@ namespace Mono.Xml
                                goto case WriteState.Closed;
                        case WriteState.Element:
                                CloseStartElement ();
+                               CheckMixedContentState ();
                                break;
                        case WriteState.Content:
+                               CheckMixedContentState ();
                                break;
                        }
-
-                       if (open_count > 0 &&
-                           state != WriteState.Attribute)
-                               elements [open_count - 1].HasSimple = true;
                }
 
                void WriteEscapedString (string text, bool isAttribute)
@@ -1307,24 +1381,51 @@ namespace Mono.Xml
                        int idx = text.IndexOfAny (escaped);
                        if (idx >= 0) {
                                char [] arr = text.ToCharArray ();
-                               writer.Write (arr, 0, idx);
+                               WriteCheckedBuffer (arr, 0, idx);
                                WriteEscapedBuffer (
                                        arr, idx, arr.Length - idx, isAttribute);
                        } else {
-                               writer.Write (text);
+                               WriteCheckedString (text);
                        }
                }
 
-               void WriteEscapedBuffer (char [] text, int index, int length,
-                       bool isAttribute)
+               void WriteCheckedString (string s)
                {
+                       int i = XmlChar.IndexOfInvalid (s, true);
+                       if (i >= 0) {
+                               char [] arr = s.ToCharArray ();
+                               writer.Write (arr, 0, i);
+                               WriteCheckedBuffer (arr, i, arr.Length - i);
+                       } else {
+                               // no invalid character.
+                               writer.Write (s);
+                       }
+               }
 
-                       if (check_character_validity) {
-                               int idx = XmlChar.IndexOfInvalid (text, index, length, true);
-                               if (idx >= 0)
+               void WriteCheckedBuffer (char [] text, int idx, int length)
+               {
+                       int start = idx;
+                       int end = idx + length;
+                       while ((idx = XmlChar.IndexOfInvalid (text, start, length, true)) >= 0) {
+                               if (check_character_validity) // actually this is one time pass.
                                        throw ArgumentError (String.Format ("Input contains invalid character at {0} : &#x{1:X};", idx, (int) text [idx]));
+                               if (start < idx)
+                                       writer.Write (text, start, idx - start);
+                               writer.Write ("&#x");
+                               writer.Write (((int) text [idx]).ToString (
+                                       "X",
+                                       CultureInfo.InvariantCulture));
+                               writer.Write (';');
+                               length -= idx - start + 1;
+                               start = idx + 1;
                        }
+                       if (start < end)
+                               writer.Write (text, start, end - start);
+               }
 
+               void WriteEscapedBuffer (char [] text, int index, int length,
+                       bool isAttribute)
+               {
                        int start = index;
                        int end = index + length;
                        for (int i = start; i < end; i++) {
@@ -1335,7 +1436,7 @@ namespace Mono.Xml
                                case '<':
                                case '>':
                                        if (start < i)
-                                               writer.Write (text, start, i - start);
+                                               WriteCheckedBuffer (text, start, i - start);
                                        writer.Write ('&');
                                        switch (text [i]) {
                                        case '&': writer.Write ("amp;"); break;
@@ -1356,7 +1457,7 @@ namespace Mono.Xml
                                        goto case '\n';
                                case '\n':
                                        if (start < i)
-                                               writer.Write (text, start, i - start);
+                                               WriteCheckedBuffer (text, start, i - start);
                                        if (isAttribute) {
                                                writer.Write (text [i] == '\r' ?
                                                        "&#xD;" : "&#xA;");
@@ -1379,7 +1480,7 @@ namespace Mono.Xml
                                start = i + 1;
                        }
                        if (start < end)
-                               writer.Write (text, start, end - start);
+                               WriteCheckedBuffer (text, start, end - start);
                }
 
                // Exceptions