Fix for the standalone tests: attribset_attribset24 and attribset_attribset40.
[mono.git] / mcs / class / System.XML / System.Xml / XmlTextWriter2.cs
1 //
2 // XmlTextWriter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006 Novell, Inc.
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections;
32 using System.Globalization;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36
37 /*
38
39 This is a fresh implementation of XmlTextWriter since Mono 1.1.14.
40
41 Here are some implementation notes (mostly common to previous module):
42
43 - WriteProcessingInstruction() does not reject 'X' 'M' 'L'
44
45         XmlWriter violates section 2.6 of W3C XML 1.0 specification (3rd. 
46         edition) since it incorrectly allows such PI target that consists of
47         case-insensitive sequence of 'X' - 'M' - 'L'. This is XmlWriter API
48         design failure which does not provide perfect WriteStartDocument().
49
50 - XmlTextWriter does not escape trailing ']' in internal subset.
51
52         The fact is as this subsection title shows. It means, to make an
53         XmlWriter compatible with other XmlWriters, it should always escape
54         the trailing ']' of the input, but XmlTextWriter runs no check.
55
56 - Prefix autogeneration for global attributes
57
58         When an attribute has a non-empty namespace URI, the prefix must be
59         non-empty string (since if the prefix is empty it is regarded as a
60         local attribute). In such case, a dummy prefix must be created.
61
62         Since attributes are written to TextWriter almost immediately, the
63         same prefix might appear in the later attributes.
64
65 - Namespace context
66
67         Namespace handling in XmlTextWriter is pretty nasty.
68
69         First of all, if WriteStartElement() takes null namespaceURI, then
70         the element has no explicit namespace and it is treated as if
71         Namespaces property were set as false.
72
73         Namespace context is structured by some writer methods:
74
75         - WriteStartElement() : If it has a non-empty argument prefix, then
76           the new prefix is bound to the argument namespaceURI. If prefix
77           is "" and namespaceURI is not empty, then it consists of a
78           default namespace.
79
80         - WriteStartAttribute() : there are two namespace provisions here:
81           1) like WriteStartElement() prefix and namespaceURI are not empty
82           2) prefix is "xmlns", or localName is "xmlns" and prefix is ""
83           If prefix is "" and namespaceURI is not empty, then the prefix is
84           "mocked up" (since an empty prefix is not possible for attributes).
85
86         - WriteQualifiedName() : the argument name and namespaceURI creates
87           a new namespace mapping. Note that default namespace (prefix "")
88           is not constructed at the state of WriteState.Attribute.
89
90         Note that WriteElementString() internally calls WriteStartElement()
91         and WriteAttributeString() internally calls WriteStartAttribute().
92
93         Sometimes those namespace outputs are in conflict. For example, if
94
95                 w.WriteStartElement ("p", "foo", "urn:foo");
96                 w.WriteStartAttribute ("xmlns", "p", "urn:bar");
97                 w.WriteEndElement ();
98
99         urn:foo will be lost.
100
101         Here are the rules:
102
103         - If either prefix or localName is explicitly "xmlns" in
104           WriteStartAttribute(), it takes the highest precedence.
105         - For WriteStartElement(), prefix is always preserved, but
106           namespaceURI context might not (because of the rule above).
107         - For WriteStartAttribute(), prefix is preserved only if there is
108           no previous mapping in the local element. If it is in conflict,
109           a new prefix is "mocked up" like an empty prefix.
110
111 - DetermineAttributePrefix(): local mapping overwrite
112
113         (do not change this section title unless you also change cross
114         references in this file.)
115
116         Even if the prefix is already mapped to another namespace, it might
117         be overridable because the conflicting mapping might reside in one
118         of the ancestors.
119
120         To check it, we once try to remove existing mapping. If it is 
121         successfully removed, then the mapping is locally added. In that
122         case, we cannot override it, so mock another prefix up.
123
124
125 - Attribute value preservation
126
127         Since xmlns and xml:* attributes are used to determine some public
128         behaviors such as XmlLang, XmlSpace and LookupPrefix(), it must
129         preserve what value is being written. At the same time, users might
130         call WriteString(), WhiteEntityRef() etc. separately, in such cases
131         we must preserve what is output to the stream.
132
133         This preservation is done using a "temporary preservation buffer",
134         the output Flush() behavior is different from MS. In such case that
135         XmlTextWriter uses that buffer, it won't be write anything until
136         XmlTextWriter.WriteEndAttribute() is called. If we implement it like
137         MS, it results in meaningless performance loss (it is not something 
138         people should expect. There is no solid behavior on when start tag 
139         closing '>' is written).
140
141 */
142
143
144 #if NET_1_1
145 namespace System.Xml
146 #else
147 namespace Mono.Xml
148 #endif
149 {
150         public class XmlTextWriter : XmlWriter
151         {
152                 // Static/constant members.
153
154                 const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
155                 const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
156
157                 static readonly Encoding unmarked_utf8encoding =
158                         new UTF8Encoding (false, false);
159                 static char [] escaped_text_chars;
160                 static char [] escaped_attr_chars;
161
162                 // Internal classes
163
164                 class XmlNodeInfo
165                 {
166                         public string Prefix;
167                         public string LocalName;
168                         public string NS;
169                         public bool HasSimple;
170                         public bool HasElements;
171                         public string XmlLang;
172                         public XmlSpace XmlSpace;
173                 }
174
175                 internal class StringUtil
176                 {
177                         static CultureInfo cul = CultureInfo.InvariantCulture;
178                         static CompareInfo cmp =
179                                 CultureInfo.InvariantCulture.CompareInfo;
180
181                         public static int IndexOf (string src, string target)
182                         {
183                                 return cmp.IndexOf (src, target);
184                         }
185
186                         public static int Compare (string s1, string s2)
187                         {
188                                 return cmp.Compare (s1, s2);
189                         }
190
191                         public static string Format (
192                                 string format, params object [] args)
193                         {
194                                 return String.Format (cul, format, args);
195                         }
196                 }
197
198                 enum XmlDeclState {
199                         Allow,
200                         Ignore,
201                         Auto,
202                         Prohibit,
203                 }
204
205                 // Instance fields
206
207                 Stream base_stream;
208                 TextWriter source; // the input TextWriter to .ctor().
209                 TextWriter writer;
210                 // It is used for storing xml:space, xml:lang and xmlns values.
211                 StringWriter preserver;
212                 string preserved_name;
213                 bool is_preserved_xmlns;
214
215                 bool allow_doc_fragment;
216                 bool close_output_stream = true;
217                 bool ignore_encoding;
218                 bool namespaces = true;
219                 XmlDeclState xmldecl_state = XmlDeclState.Allow;
220
221                 bool check_character_validity;
222                 NewLineHandling newline_handling = NewLineHandling.None;
223
224                 bool is_document_entity;
225                 WriteState state = WriteState.Start;
226                 XmlNodeType node_state = XmlNodeType.None;
227                 XmlNamespaceManager nsmanager;
228                 int open_count;
229                 XmlNodeInfo [] elements = new XmlNodeInfo [10];
230                 Stack new_local_namespaces = new Stack ();
231                 ArrayList explicit_nsdecls = new ArrayList ();
232
233                 bool indent;
234                 int indent_count = 2;
235                 char indent_char = ' ';
236                 string indent_string = "  ";
237                 string newline;
238                 bool indent_attributes;
239
240                 char quote_char = '"';
241
242                 // Constructors
243
244                 public XmlTextWriter (string filename, Encoding encoding)
245                         : this (new FileStream (filename, FileMode.Create, FileAccess.Write, FileShare.None), encoding)
246                 {
247                 }
248
249                 public XmlTextWriter (Stream stream, Encoding encoding)
250                         : this (new StreamWriter (stream,
251                                 encoding == null ? unmarked_utf8encoding : encoding))
252                 {
253                         ignore_encoding = (encoding == null);
254                         Initialize (writer);
255                         allow_doc_fragment = true;
256                 }
257
258                 public XmlTextWriter (TextWriter writer)
259                 {
260                         Initialize (writer);
261                         allow_doc_fragment = true;
262                 }
263
264 #if NET_2_0
265                 internal XmlTextWriter (
266                         TextWriter writer, XmlWriterSettings settings, bool closeOutput)
267                 {
268                         if (settings == null)
269                                 settings = new XmlWriterSettings ();
270
271                         Initialize (writer);
272
273                         close_output_stream = closeOutput;
274                         allow_doc_fragment =
275                                 settings.ConformanceLevel != ConformanceLevel.Document;
276                         switch (settings.ConformanceLevel) {
277                         case ConformanceLevel.Auto:
278                                 xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Allow;
279                                 break;
280                         case ConformanceLevel.Document:
281                                 // LAMESPEC:
282                                 // On MSDN, XmlWriterSettings.OmitXmlDeclaration is documented as:
283                                 // "The XML declaration is always written if
284                                 //  ConformanceLevel is set to Document, even 
285                                 //  if OmitXmlDeclaration is set to true. "
286                                 // but it is incorrect. It does consider 
287                                 // OmitXmlDeclaration property.
288                                 xmldecl_state = settings.OmitXmlDeclaration ? XmlDeclState.Ignore : XmlDeclState.Auto;
289                                 break;
290                         case ConformanceLevel.Fragment:
291                                 xmldecl_state = XmlDeclState.Prohibit;
292                                 break;
293                         }
294                         if (settings.Indent)
295                                 Formatting = Formatting.Indented;
296                         indent_string = settings.IndentChars == null ?
297                                 String.Empty : settings.IndentChars;
298                         if (settings.NewLineChars != null)
299                                 newline = settings.NewLineChars;
300                         indent_attributes = settings.NewLineOnAttributes;
301
302                         check_character_validity = settings.CheckCharacters;
303                         newline_handling = settings.NewLineHandling;
304                 }
305 #endif
306
307                 void Initialize (TextWriter writer)
308                 {
309                         if (writer == null)
310                                 throw new ArgumentNullException ("writer");
311                         XmlNameTable name_table = new NameTable ();
312                         this.writer = writer;
313                         if (writer is StreamWriter)
314                                 base_stream = ((StreamWriter) writer).BaseStream;
315                         source = writer;
316                         nsmanager = new XmlNamespaceManager (name_table);
317                         newline = writer.NewLine;
318
319                         escaped_text_chars =
320                                 newline_handling != NewLineHandling.None ?
321                                 new char [] {'&', '<', '>', '\r', '\n'} :
322                                 new char [] {'&', '<', '>'};
323                         escaped_attr_chars =
324                                 new char [] {'"', '&', '<', '>', '\r', '\n'};
325                 }
326
327 #if NET_2_0
328                 // 2.0 XmlWriterSettings support
329
330                 // As for ConformanceLevel, MS.NET is inconsistent with
331                 // MSDN documentation. For example, even if ConformanceLevel
332                 // is set as .Auto, multiple WriteStartDocument() calls
333                 // result in an error.
334                 // ms-help://MS.NETFramework.v20.en/wd_xml/html/7db8802b-53d8-4735-a637-4d2d2158d643.htm
335
336 #endif
337
338                 // Literal Output Control
339
340                 public Formatting Formatting {
341                         get { return indent ? Formatting.Indented : Formatting.None; }
342                         set {
343                                 // Someone thinks it should be settable even
344                                 // after writing some content (bug #78148).
345                                 // I totally disagree but here is the fix.
346
347                                 //if (state != WriteState.Start)
348                                 //      throw InvalidOperation ("Formatting must be set before it is actually used to write output.");
349                                 indent = (value == Formatting.Indented);
350                         }
351                 }
352
353                 public int Indentation {
354                         get { return indent_count; }
355                         set {
356                                 if (value < 0)
357                                         throw ArgumentError ("Indentation must be non-negative integer.");
358                                 indent_count = value;
359                                 indent_string = value == 0 ? String.Empty :
360                                         new string (indent_char, indent_count);
361                         }
362                 }
363
364                 public char IndentChar {
365                         get { return indent_char; }
366                         set {
367                                 indent_char = value;
368                                 indent_string = new string (indent_char, indent_count);
369                         }
370                 }
371
372                 public char QuoteChar {
373                         get { return quote_char; }
374                         set {
375                                 if (state == WriteState.Attribute)
376                                         throw InvalidOperation ("QuoteChar must not be changed inside attribute value.");
377                                 if ((value != '\'') && (value != '\"'))
378                                         throw ArgumentError ("Only ' and \" are allowed as an attribute quote character.");
379                                 quote_char = value;
380                                 escaped_attr_chars [0] = quote_char;
381                         }
382                 }
383
384                 // Context Retriever
385
386                 public override string XmlLang {
387                         get { return open_count == 0 ? null : elements [open_count - 1].XmlLang; }
388                 }
389
390                 public override XmlSpace XmlSpace {
391                         get { return open_count == 0 ? XmlSpace.None : elements [open_count - 1].XmlSpace; }
392                 }
393
394                 public override WriteState WriteState {
395                         get { return state; }
396                 }
397
398                 public override string LookupPrefix (string namespaceUri)
399                 {
400                         if (namespaceUri == null || namespaceUri == String.Empty)
401                                 throw ArgumentError ("The Namespace cannot be empty.");
402
403                         if (namespaceUri == nsmanager.DefaultNamespace)
404                                 return String.Empty;
405
406                         string prefix = nsmanager.LookupPrefixExclusive (
407                                 namespaceUri, false);
408
409                         // XmlNamespaceManager has changed to return null
410                         // when NSURI not found.
411                         // (Contradiction to the ECMA documentation.)
412                         return prefix;
413                 }
414
415                 // Stream Control
416
417                 public Stream BaseStream {
418                         get { return base_stream; }
419                 }
420
421                 public override void Close ()
422                 {
423 #if NET_2_0
424                         if (state != WriteState.Error) {
425 #endif
426                                 if (state == WriteState.Attribute)
427                                         WriteEndAttribute ();
428                                 while (open_count > 0)
429                                         WriteEndElement ();
430 #if NET_2_0
431                         }
432 #endif
433
434                         if (close_output_stream)
435                                 writer.Close ();
436                         else
437                                 writer.Flush ();
438                         state = WriteState.Closed;
439                 }
440
441                 public override void Flush ()
442                 {
443                         writer.Flush ();
444                 }
445
446                 // Misc Control
447                 public bool Namespaces {
448                         get { return namespaces; }
449                         set {
450                                 if (state != WriteState.Start)
451                                         throw InvalidOperation ("This property must be set before writing output.");
452                                 namespaces = value;
453                         }
454                 }
455
456                 // XML Declaration
457
458                 public override void WriteStartDocument ()
459                 {
460                         WriteStartDocumentCore (false, false);
461                         is_document_entity = true;
462                 }
463
464                 public override void WriteStartDocument (bool standalone)
465                 {
466                         WriteStartDocumentCore (true, standalone);
467                         is_document_entity = true;
468                 }
469
470                 void WriteStartDocumentCore (bool outputStd, bool standalone)
471                 {
472                         if (state != WriteState.Start)
473                                 throw StateError ("XmlDeclaration");
474
475                         switch (xmldecl_state) {
476                         case XmlDeclState.Ignore:
477                                 return;
478                         case XmlDeclState.Prohibit:
479                                 throw InvalidOperation ("WriteStartDocument cannot be called when ConformanceLevel is Fragment.");
480                         }
481
482                         state = WriteState.Prolog;
483
484                         writer.Write ("<?xml version=");
485                         writer.Write (quote_char);
486                         writer.Write ("1.0");
487                         writer.Write (quote_char);
488                         if (!ignore_encoding) {
489                                 writer.Write (" encoding=");
490                                 writer.Write (quote_char);
491                                 writer.Write (writer.Encoding.WebName);
492                                 writer.Write (quote_char);
493                         }
494                         if (outputStd) {
495                                 writer.Write (" standalone=");
496                                 writer.Write (quote_char);
497                                 writer.Write (standalone ? "yes" : "no");
498                                 writer.Write (quote_char);
499                         }
500                         writer.Write ("?>");
501
502                         xmldecl_state = XmlDeclState.Ignore;
503                 }
504
505                 public override void WriteEndDocument ()
506                 {
507                         switch (state) {
508 #if NET_2_0
509                         case WriteState.Error:
510 #endif
511                         case WriteState.Closed:
512                         case WriteState.Start:
513                                 throw StateError ("EndDocument");
514                         }
515
516                         if (state == WriteState.Attribute)
517                                 WriteEndAttribute ();
518                         while (open_count > 0)
519                                 WriteEndElement ();
520
521                         state = WriteState.Start;
522                         is_document_entity = false;
523                 }
524
525                 // DocType Declaration
526
527                 public override void WriteDocType (string name,
528                         string pubid, string sysid, string subset)
529                 {
530                         if (name == null)
531                                 throw ArgumentError ("name");
532                         if (!XmlChar.IsName (name))
533                                 throw ArgumentError ("name");
534
535                         if (node_state != XmlNodeType.None)
536                                 throw StateError ("DocType");
537                         node_state = XmlNodeType.DocumentType;
538
539                         if (xmldecl_state == XmlDeclState.Auto)
540                                 OutputAutoStartDocument ();
541
542                         WriteIndent ();
543
544                         writer.Write ("<!DOCTYPE ");
545                         writer.Write (name);
546                         if (pubid != null) {
547                                 writer.Write (" PUBLIC ");
548                                 writer.Write (quote_char);
549                                 writer.Write (pubid);
550                                 writer.Write (quote_char);
551                                 writer.Write (' ');
552                                 writer.Write (quote_char);
553                                 if (sysid != null)
554                                         writer.Write (sysid);
555                                 writer.Write (quote_char);
556                         }
557                         else if (sysid != null) {
558                                 writer.Write (" SYSTEM ");
559                                 writer.Write (quote_char);
560                                 writer.Write (sysid);
561                                 writer.Write (quote_char);
562                         }
563
564                         if (subset != null) {
565                                 writer.Write ("[");
566                                 // LAMESPEC: see the top of this source.
567                                 writer.Write (subset);
568                                 writer.Write ("]");
569                         }
570                         writer.Write ('>');
571
572                         state = WriteState.Prolog;
573                 }
574
575                 // StartElement
576
577                 public override void WriteStartElement (
578                         string prefix, string localName, string namespaceUri)
579                 {
580 #if NET_2_0
581                         if (state == WriteState.Error || state == WriteState.Closed)
582 #else
583                         if (state == WriteState.Closed)
584 #endif
585                                 throw StateError ("StartTag");
586                         node_state = XmlNodeType.Element;
587
588                         bool anonPrefix = (prefix == null);
589                         if (prefix == null)
590                                 prefix = String.Empty;
591
592                         // Crazy namespace check goes here.
593                         //
594                         // 1. if Namespaces is false, then any significant 
595                         //    namespace indication is not allowed.
596                         // 2. if Prefix is non-empty and NamespaceURI is
597                         //    empty, it is an error in 1.x, or it is reset to
598                         //    an empty string in 2.0.
599                         // 3. null NamespaceURI indicates that namespace is
600                         //    not considered.
601                         // 4. prefix must not be equivalent to "XML" in
602                         //    case-insensitive comparison.
603                         if (!namespaces && namespaceUri != null && namespaceUri.Length > 0)
604                                 throw ArgumentError ("Namespace is disabled in this XmlTextWriter.");
605                         if (!namespaces && prefix.Length > 0)
606                                 throw ArgumentError ("Namespace prefix is disabled in this XmlTextWriter.");
607
608                         if (prefix.Length > 0 && namespaceUri == null)
609                                 throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
610                         // Considering the fact that WriteStartAttribute()
611                         // automatically changes argument namespaceURI, this
612                         // is kind of silly implementation. See bug #77094.
613                         if (namespaces &&
614                             prefix != null && prefix.Length == 3 &&
615                             namespaceUri != XmlNamespace &&
616                             (prefix [0] == 'x' || prefix [0] == 'X') &&
617                             (prefix [1] == 'm' || prefix [1] == 'M') &&
618                             (prefix [2] == 'l' || prefix [2] == 'L'))
619                                 throw new ArgumentException ("A prefix cannot be equivalent to \"xml\" in case-insensitive match.");
620
621
622                         if (xmldecl_state == XmlDeclState.Auto)
623                                 OutputAutoStartDocument ();
624                         if (state == WriteState.Element)
625                                 CloseStartElement ();
626                         if (open_count > 0)
627                                 elements [open_count - 1].HasElements = true;
628
629                         nsmanager.PushScope ();
630
631                         if (namespaces && namespaceUri != null) {
632                                 // If namespace URI is empty, then prefix must 
633                                 // be empty as well.
634                                 if (anonPrefix && namespaceUri.Length > 0)
635                                         prefix = LookupPrefix (namespaceUri);
636                                 if (prefix == null || namespaceUri.Length == 0)
637                                         prefix = String.Empty;
638                         }
639
640                         WriteIndent ();
641
642                         writer.Write ("<");
643
644                         if (prefix.Length > 0) {
645                                 writer.Write (prefix);
646                                 writer.Write (':');
647                         }
648                         writer.Write (localName);
649
650                         if (elements.Length == open_count) {
651                                 XmlNodeInfo [] tmp = new XmlNodeInfo [open_count << 1];
652                                 Array.Copy (elements, tmp, open_count);
653                                 elements = tmp;
654                         }
655                         if (elements [open_count] == null)
656                                 elements [open_count] =
657                                         new XmlNodeInfo ();
658                         XmlNodeInfo info = elements [open_count];
659                         info.Prefix = prefix;
660                         info.LocalName = localName;
661                         info.NS = namespaceUri;
662                         info.HasSimple = false;
663                         info.HasElements = false;
664                         info.XmlLang = XmlLang;
665                         info.XmlSpace = XmlSpace;
666                         open_count++;
667
668                         if (namespaces && namespaceUri != null) {
669                                 string oldns = nsmanager.LookupNamespace (prefix, false);
670                                 if (oldns != namespaceUri) {
671                                         nsmanager.AddNamespace (prefix, namespaceUri);
672                                         new_local_namespaces.Push (prefix);
673                                 }
674                         }
675
676                         state = WriteState.Element;
677                 }
678
679                 void CloseStartElement ()
680                 {
681                         CloseStartElementCore ();
682
683                         if (state == WriteState.Element)
684                                 writer.Write ('>');
685                         state = WriteState.Content;
686                 }
687
688                 void CloseStartElementCore ()
689                 {
690                         if (state == WriteState.Attribute)
691                                 WriteEndAttribute ();
692
693                         if (new_local_namespaces.Count == 0) {
694                                 if (explicit_nsdecls.Count > 0)
695                                         explicit_nsdecls.Clear ();
696                                 return;
697                         }
698
699                         // Missing xmlns attributes are added to 
700                         // explicit_nsdecls (it is cleared but this way
701                         // I save another array creation).
702                         int idx = explicit_nsdecls.Count;
703                         while (new_local_namespaces.Count > 0) {
704                                 string p = (string) new_local_namespaces.Pop ();
705                                 bool match = false;
706                                 for (int i = 0; i < explicit_nsdecls.Count; i++) {
707                                         if ((string) explicit_nsdecls [i] == p) {
708                                                 match = true;
709                                                 break;
710                                         }
711                                 }
712                                 if (match)
713                                         continue;
714                                 explicit_nsdecls.Add (p);
715                         }
716
717                         for (int i = idx; i < explicit_nsdecls.Count; i++) {
718                                 string prefix = (string) explicit_nsdecls [i];
719                                 string ns = nsmanager.LookupNamespace (prefix, false);
720                                 if (ns == null)
721                                         continue; // superceded
722                                 if (prefix.Length > 0) {
723                                         writer.Write (" xmlns:");
724                                         writer.Write (prefix);
725                                 } else {
726                                         writer.Write (" xmlns");
727                                 }
728                                 writer.Write ('=');
729                                 writer.Write (quote_char);
730                                 WriteEscapedString (ns, true);
731                                 writer.Write (quote_char);
732                         }
733                         explicit_nsdecls.Clear ();
734                 }
735
736                 // EndElement
737
738                 public override void WriteEndElement ()
739                 {
740                         WriteEndElementCore (false);
741                 }
742
743                 public override void WriteFullEndElement ()
744                 {
745                         WriteEndElementCore (true);
746                 }
747
748                 void WriteEndElementCore (bool full)
749                 {
750 #if NET_2_0
751                         if (state == WriteState.Error || state == WriteState.Closed)
752 #else
753                         if (state == WriteState.Closed)
754 #endif
755                                 throw StateError ("EndElement");
756                         if (open_count == 0)
757                                 throw InvalidOperation ("There is no more open element.");
758
759                         bool isEmpty = state != WriteState.Content;
760
761                         CloseStartElementCore ();
762
763                         nsmanager.PopScope ();
764
765                         if (state == WriteState.Element) {
766                                 if (full)
767                                         writer.Write ('>');
768                                 else
769                                         writer.Write (" />");
770                         }
771
772                         if (full || state == WriteState.Content)
773                                 WriteIndentEndElement ();
774
775                         XmlNodeInfo info = elements [--open_count];
776
777                         if (full || state == WriteState.Content) {
778                                 writer.Write ("</");
779                                 if (info.Prefix.Length > 0) {
780                                         writer.Write (info.Prefix);
781                                         writer.Write (':');
782                                 }
783                                 writer.Write (info.LocalName);
784                                 writer.Write ('>');
785                         }
786
787                         state = WriteState.Content;
788                         if (open_count == 0)
789                                 node_state = XmlNodeType.EndElement;
790                 }
791
792                 // Attribute
793
794                 public override void WriteStartAttribute (
795                         string prefix, string localName, string namespaceUri)
796                 {
797                         // LAMESPEC: this violates the expected behavior of
798                         // this method, as it incorrectly allows unbalanced
799                         // output of attributes. Microfot changes description
800                         // on its behavior at their will, regardless of
801                         // ECMA description.
802                         if (state == WriteState.Attribute)
803                                 WriteEndAttribute ();
804
805                         if (state != WriteState.Element && state != WriteState.Start)
806                                 throw StateError ("Attribute");
807
808                         if ((object) prefix == null)
809                                 prefix = String.Empty;
810
811                         // For xmlns URI, prefix is forced to be "xmlns"
812                         bool isNSDecl = false;
813                         if (namespaceUri == XmlnsNamespace) {
814                                 isNSDecl = true;
815                                 if (prefix.Length == 0 && localName != "xmlns")
816                                         prefix = "xmlns";
817                         }
818                         else
819                                 isNSDecl = (prefix == "xmlns" ||
820                                         localName == "xmlns" && prefix.Length == 0);
821
822                         if (namespaces) {
823                                 // MS implementation is pretty hacky here. 
824                                 // Regardless of namespace URI it is regarded
825                                 // as NS URI for "xml".
826                                 if (prefix == "xml")
827                                         namespaceUri = XmlNamespace;
828                                 // infer namespace URI.
829                                 else if ((object) namespaceUri == null) {
830                                         if (isNSDecl)
831                                                 namespaceUri = XmlnsNamespace;
832                                         else
833                                                 namespaceUri = String.Empty;
834                                 }
835
836                                 // It is silly design - null namespace with
837                                 // "xmlns" are allowed (for namespace-less
838                                 // output; while there is Namespaces property)
839                                 // On the other hand, namespace "" is not 
840                                 // allowed.
841                                 if (isNSDecl && namespaceUri != XmlnsNamespace)
842                                         throw ArgumentError (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
843
844                                 // If namespace URI is empty, then prefix
845                                 // must be empty as well.
846                                 if (prefix.Length > 0 && namespaceUri.Length == 0)
847                                         throw ArgumentError ("Namespace URI must not be null when prefix is not an empty string.");
848
849                                 // Dive into extremely complex procedure.
850                                 if (!isNSDecl && namespaceUri.Length > 0)
851                                         prefix = DetermineAttributePrefix (
852                                                 prefix, localName, namespaceUri);
853                         }
854
855                         if (indent_attributes)
856                                 WriteIndentAttribute ();
857                         else if (state != WriteState.Start)
858                                 writer.Write (' ');
859
860                         if (prefix.Length > 0) {
861                                 writer.Write (prefix);
862                                 writer.Write (':');
863                         }
864                         writer.Write (localName);
865                         writer.Write ('=');
866                         writer.Write (quote_char);
867
868                         if (isNSDecl || prefix == "xml") {
869                                 if (preserver == null)
870                                         preserver = new StringWriter ();
871                                 else
872                                         preserver.GetStringBuilder ().Length = 0;
873                                 writer = preserver;
874
875                                 if (!isNSDecl) {
876                                         is_preserved_xmlns = false;
877                                         preserved_name = localName;
878                                 } else {
879                                         is_preserved_xmlns = true;
880                                         preserved_name = localName == "xmlns" ? 
881                                                 String.Empty : localName;
882                                 }
883                         }
884
885                         state = WriteState.Attribute;
886                 }
887
888                 // See also:
889                 // "DetermineAttributePrefix(): local mapping overwrite"
890                 string DetermineAttributePrefix (
891                         string prefix, string local, string ns)
892                 {
893                         bool mockup = false;
894                         if (prefix.Length == 0) {
895                                 prefix = LookupPrefix (ns);
896                                 if (prefix != null && prefix.Length > 0)
897                                         return prefix;
898                                 mockup = true;
899                         } else {
900                                 prefix = nsmanager.NameTable.Add (prefix);
901                                 string existing = nsmanager.LookupNamespace (prefix, true);
902                                 if (existing == ns)
903                                         return prefix;
904                                 if (existing != null) {
905                                         // See code comment on the head of
906                                         // this source file.
907                                         nsmanager.RemoveNamespace (prefix, existing);
908                                         if (nsmanager.LookupNamespace (prefix, true) != existing) {
909                                                 mockup = true;
910                                                 nsmanager.AddNamespace (prefix, existing);
911                                         }
912                                 }
913                         }
914
915                         if (mockup)
916                                 prefix = MockupPrefix (ns, true);
917                         new_local_namespaces.Push (prefix);
918                         nsmanager.AddNamespace (prefix, ns);
919
920                         return prefix;
921                 }
922
923                 string MockupPrefix (string ns, bool skipLookup)
924                 {
925                         string prefix = skipLookup ? null :
926                                 LookupPrefix (ns);
927                         if (prefix != null && prefix.Length > 0)
928                                 return prefix;
929                         for (int p = 1; ; p++) {
930                                 prefix = StringUtil.Format ("d{0}p{1}", open_count, p);
931                                 if (new_local_namespaces.Contains (prefix))
932                                         continue;
933                                 if (null != nsmanager.LookupNamespace (
934                                         nsmanager.NameTable.Get (prefix)))
935                                         continue;
936                                 nsmanager.AddNamespace (prefix, ns);
937                                 new_local_namespaces.Push (prefix);
938                                 return prefix;
939                         }
940                 }
941
942                 public override void WriteEndAttribute ()
943                 {
944                         if (state != WriteState.Attribute)
945                                 throw StateError ("End of attribute");
946
947                         if (writer == preserver) {
948                                 writer = source;
949                                 string value = preserver.ToString ();
950                                 if (is_preserved_xmlns) {
951                                         if (preserved_name.Length > 0 &&
952                                             value.Length == 0)
953                                                 throw ArgumentError ("Non-empty prefix must be mapped to non-empty namespace URI.");
954                                         string existing = nsmanager.LookupNamespace (preserved_name, false);
955                                         explicit_nsdecls.Add (preserved_name);
956                                         if (open_count > 0 &&
957                                             elements [open_count - 1].NS == String.Empty &&
958                                             elements [open_count - 1].Prefix == preserved_name)
959                                                 ; // do nothing
960                                         else if (existing != value)
961                                                 nsmanager.AddNamespace (preserved_name, value);
962                                 } else {
963                                         switch (preserved_name) {
964                                         case "lang":
965                                                 if (open_count > 0)
966                                                         elements [open_count - 1].XmlLang = value;
967                                                 break;
968                                         case "space":
969                                                 switch (value) {
970                                                 case "default":
971                                                         if (open_count > 0)
972                                                                 elements [open_count - 1].XmlSpace = XmlSpace.Default;
973                                                         break;
974                                                 case "preserve":
975                                                         if (open_count > 0)
976                                                                 elements [open_count - 1].XmlSpace = XmlSpace.Preserve;
977                                                         break;
978                                                 default:
979                                                         throw ArgumentError ("Invalid value for xml:space.");
980                                                 }
981                                                 break;
982                                         }
983                                 }
984                                 writer.Write (value);
985                         }
986
987                         writer.Write (quote_char);
988                         state = WriteState.Element;
989                 }
990
991                 // Non-Text Content
992
993                 public override void WriteComment (string text)
994                 {
995                         if (text == null)
996                                 throw ArgumentError ("text");
997
998                         if (text.Length > 0 && text [text.Length - 1] == '-')
999                                 throw ArgumentError ("An input string to WriteComment method must not end with '-'. Escape it with '&#2D;'.");
1000                         if (StringUtil.IndexOf (text, "--") > 0)
1001                                 throw ArgumentError ("An XML comment cannot end with \"-\".");
1002
1003                         if (state == WriteState.Attribute || state == WriteState.Element)
1004                                 CloseStartElement ();
1005
1006                         WriteIndent ();
1007
1008                         ShiftStateTopLevel ("Comment", false, false, false);
1009
1010                         writer.Write ("<!--");
1011                         writer.Write (text);
1012                         writer.Write ("-->");
1013                 }
1014
1015                 // LAMESPEC: see comments on the top of this source.
1016                 public override void WriteProcessingInstruction (string name, string text)
1017                 {
1018                         if (name == null)
1019                                 throw ArgumentError ("name");
1020                         if (text == null)
1021                                 throw ArgumentError ("text");
1022
1023                         WriteIndent ();
1024
1025                         if (!XmlChar.IsName (name))
1026                                 throw ArgumentError ("A processing instruction name must be a valid XML name.");
1027
1028                         if (StringUtil.IndexOf (text, "?>") > 0)
1029                                 throw ArgumentError ("Processing instruction cannot contain \"?>\" as its value.");
1030
1031                         ShiftStateTopLevel ("ProcessingInstruction", false, name == "xml", false);
1032
1033                         writer.Write ("<?");
1034                         writer.Write (name);
1035                         writer.Write (' ');
1036                         writer.Write (text);
1037                         writer.Write ("?>");
1038
1039                         if (state == WriteState.Start)
1040                                 state = WriteState.Prolog;
1041                 }
1042
1043                 // Text Content
1044
1045                 public override void WriteWhitespace (string text)
1046                 {
1047                         if (text == null)
1048                                 throw ArgumentError ("text");
1049
1050                         // huh? Shouldn't it accept an empty string???
1051                         if (text.Length == 0 ||
1052                             XmlChar.IndexOfNonWhitespace (text) >= 0)
1053                                 throw ArgumentError ("WriteWhitespace method accepts only whitespaces.");
1054
1055                         ShiftStateTopLevel ("Whitespace", true, false, true);
1056
1057                         writer.Write (text);
1058                 }
1059
1060                 public override void WriteCData (string text)
1061                 {
1062                         if (text == null)
1063                                 text = String.Empty;
1064                         ShiftStateContent ("CData", false);
1065
1066                         if (StringUtil.IndexOf (text, "]]>") >= 0)
1067                                 throw ArgumentError ("CDATA section must not contain ']]>'.");
1068                         writer.Write ("<![CDATA[");
1069                         WriteCheckedString (text);
1070                         writer.Write ("]]>");
1071                 }
1072
1073                 public override void WriteString (string text)
1074                 {
1075                         if (text == null || text.Length == 0)
1076                                 return; // do nothing, including state transition.
1077                         ShiftStateContent ("Text", true);
1078
1079                         WriteEscapedString (text, state == WriteState.Attribute);
1080                 }
1081
1082                 public override void WriteRaw (string raw)
1083                 {
1084                         if (raw == null)
1085                                 return; // do nothing, including state transition.
1086
1087                         //WriteIndent ();
1088
1089                         // LAMESPEC: It rejects XMLDecl while it allows
1090                         // DocType which could consist of non well-formed XML.
1091                         ShiftStateTopLevel ("Raw string", true, true, true);
1092
1093                         writer.Write (raw);
1094                 }
1095
1096                 public override void WriteCharEntity (char ch)
1097                 {
1098                         WriteCharacterEntity (ch, '\0', false);
1099                 }
1100
1101                 public override void WriteSurrogateCharEntity (char low, char high)
1102                 {
1103                         WriteCharacterEntity (low, high, true);
1104                 }
1105
1106                 void WriteCharacterEntity (char ch, char high, bool surrogate)
1107                 {
1108                         if (surrogate &&
1109                             ('\uD800' > high || high > '\uDC00' ||
1110                              '\uDC00' > ch || ch > '\uDFFF'))
1111                                 throw ArgumentError (String.Format ("Invalid surrogate pair was found. Low: &#x{0:X}; High: &#x{0:X};", (int) ch, (int) high));
1112                         else if (check_character_validity && XmlChar.IsInvalid (ch))
1113                                 throw ArgumentError (String.Format ("Invalid character &#x{0:X};", (int) ch));
1114
1115                         ShiftStateContent ("Character", true);
1116
1117                         int v = surrogate ? (high - 0xD800) * 0x400 + ch - 0xDC00 + 0x10000 : (int) ch;
1118                         writer.Write ("&#x");
1119                         writer.Write (v.ToString ("X", CultureInfo.InvariantCulture));
1120                         writer.Write (';');
1121                 }
1122
1123                 public override void WriteEntityRef (string name)
1124                 {
1125                         if (name == null)
1126                                 throw ArgumentError ("name");
1127                         if (!XmlChar.IsName (name))
1128                                 throw ArgumentError ("Argument name must be a valid XML name.");
1129
1130                         ShiftStateContent ("Entity reference", true);
1131
1132                         writer.Write ('&');
1133                         writer.Write (name);
1134                         writer.Write (';');
1135                 }
1136
1137                 // Applied methods
1138
1139                 public override void WriteName (string name)
1140                 {
1141                         if (name == null)
1142                                 throw ArgumentError ("name");
1143                         if (!XmlChar.IsName (name))
1144                                 throw ArgumentError ("Not a valid name string.");
1145                         WriteString (name);
1146                 }
1147
1148                 public override void WriteNmToken (string nmtoken)
1149                 {
1150                         if (nmtoken == null)
1151                                 throw ArgumentError ("nmtoken");
1152                         if (!XmlChar.IsNmToken (nmtoken))
1153                                 throw ArgumentError ("Not a valid NMTOKEN string.");
1154                         WriteString (nmtoken);
1155                 }
1156
1157                 public override void WriteQualifiedName (
1158                         string localName, string ns)
1159                 {
1160                         if (localName == null)
1161                                 throw ArgumentError ("localName");
1162                         if (ns == null)
1163                                 ns = String.Empty;
1164
1165                         if (ns == XmlnsNamespace)
1166                                 throw ArgumentError ("Prefix 'xmlns' is reserved and cannot be overriden.");
1167                         if (!XmlChar.IsNCName (localName))
1168                                 throw ArgumentError ("localName must be a valid NCName.");
1169
1170                         ShiftStateContent ("QName", true);
1171
1172                         string prefix = ns.Length > 0 ? LookupPrefix (ns) : String.Empty;
1173                         if (prefix == null) {
1174                                 if (state == WriteState.Attribute)
1175                                         prefix = MockupPrefix (ns, false);
1176                                 else
1177                                         throw ArgumentError (String.Format ("Namespace '{0}' is not declared.", ns));
1178                         }
1179
1180                         if (prefix != String.Empty) {
1181                                 writer.Write (prefix);
1182                                 writer.Write (":");
1183                         }
1184                         writer.Write (localName);
1185                 }
1186
1187                 // Chunk data
1188
1189                 void CheckChunkRange (Array buffer, int index, int count)
1190                 {
1191                         if (buffer == null)
1192                                 throw new ArgumentNullException ("buffer");
1193                         if (index < 0 || buffer.Length < index)
1194                                 throw ArgumentOutOfRangeError ("index");
1195                         if (count < 0 || buffer.Length < index + count)
1196                                 throw ArgumentOutOfRangeError ("count");
1197                 }
1198
1199                 public override void WriteBase64 (byte [] buffer, int index, int count)
1200                 {
1201                         CheckChunkRange (buffer, index, count);
1202
1203                         WriteString (Convert.ToBase64String (buffer, index, count));
1204                 }
1205
1206                 public override void WriteBinHex (byte [] buffer, int index, int count)
1207                 {
1208                         CheckChunkRange (buffer, index, count);
1209
1210                         ShiftStateContent ("BinHex", true);
1211
1212                         XmlConvert.WriteBinHex (buffer, index, count, writer);
1213                 }
1214
1215                 public override void WriteChars (char [] buffer, int index, int count)
1216                 {
1217                         CheckChunkRange (buffer, index, count);
1218
1219                         ShiftStateContent ("Chars", true);
1220
1221                         WriteEscapedBuffer (buffer, index, count,
1222                                 state == WriteState.Attribute);
1223                 }
1224
1225                 public override void WriteRaw (char [] buffer, int index, int count)
1226                 {
1227                         CheckChunkRange (buffer, index, count);
1228
1229                         ShiftStateContent ("Raw text", false);
1230
1231                         writer.Write (buffer, index, count);
1232                 }
1233
1234                 // Utilities
1235
1236                 void WriteIndent ()
1237                 {
1238                         WriteIndentCore (0, false);
1239                 }
1240
1241                 void WriteIndentEndElement ()
1242                 {
1243                         WriteIndentCore (-1, false);
1244                 }
1245
1246                 void WriteIndentAttribute ()
1247                 {
1248                         if (!WriteIndentCore (0, true))
1249                                 writer.Write (' '); // space is required instead.
1250                 }
1251
1252                 bool WriteIndentCore (int nestFix, bool attribute)
1253                 {
1254                         if (!indent)
1255                                 return false;
1256                         for (int i = open_count - 1; i >= 0; i--)
1257                                 if (!attribute && elements [i].HasSimple)
1258                                         return false;
1259
1260                         if (state != WriteState.Start)
1261                                 writer.Write (newline);
1262                         for (int i = 0; i < open_count + nestFix; i++)
1263                                 writer.Write (indent_string);
1264                         return true;
1265                 }
1266
1267                 void OutputAutoStartDocument ()
1268                 {
1269                         if (state != WriteState.Start)
1270                                 return;
1271                         WriteStartDocumentCore (false, false);
1272                 }
1273
1274                 void ShiftStateTopLevel (string occured, bool allowAttribute, bool dontCheckXmlDecl, bool isCharacter)
1275                 {
1276                         switch (state) {
1277 #if NET_2_0
1278                         case WriteState.Error:
1279 #endif
1280                         case WriteState.Closed:
1281                                 throw StateError (occured);
1282                         case WriteState.Start:
1283                                 if (isCharacter)
1284                                         CheckMixedContentState ();
1285                                 if (xmldecl_state == XmlDeclState.Auto && !dontCheckXmlDecl)
1286                                         OutputAutoStartDocument ();
1287                                 state = WriteState.Prolog;
1288                                 break;
1289                         case WriteState.Attribute:
1290                                 if (allowAttribute)
1291                                         break;
1292                                 goto case WriteState.Closed;
1293                         case WriteState.Element:
1294                                 if (isCharacter)
1295                                         CheckMixedContentState ();
1296                                 CloseStartElement ();
1297                                 break;
1298                         case WriteState.Content:
1299                                 if (isCharacter)
1300                                         CheckMixedContentState ();
1301                                 break;
1302                         }
1303
1304                 }
1305
1306                 void CheckMixedContentState ()
1307                 {
1308 //                      if (open_count > 0 &&
1309 //                          state != WriteState.Attribute)
1310 //                              elements [open_count - 1].HasSimple = true;
1311                         if (open_count > 0)
1312                                 elements [open_count - 1].HasSimple = true;
1313                 }
1314
1315                 void ShiftStateContent (string occured, bool allowAttribute)
1316                 {
1317                         switch (state) {
1318 #if NET_2_0
1319                         case WriteState.Error:
1320 #endif
1321                         case WriteState.Closed:
1322                                         throw StateError (occured);
1323                         case WriteState.Prolog:
1324                         case WriteState.Start:
1325                                 if (!allow_doc_fragment || is_document_entity)
1326                                         goto case WriteState.Closed;
1327                                 if (xmldecl_state == XmlDeclState.Auto)
1328                                         OutputAutoStartDocument ();
1329                                 CheckMixedContentState ();
1330                                 state = WriteState.Content;
1331                                 break;
1332                         case WriteState.Attribute:
1333                                 if (allowAttribute)
1334                                         break;
1335                                 goto case WriteState.Closed;
1336                         case WriteState.Element:
1337                                 CloseStartElement ();
1338                                 CheckMixedContentState ();
1339                                 break;
1340                         case WriteState.Content:
1341                                 CheckMixedContentState ();
1342                                 break;
1343                         }
1344                 }
1345
1346                 void WriteEscapedString (string text, bool isAttribute)
1347                 {
1348                         char [] escaped = isAttribute ?
1349                                 escaped_attr_chars : escaped_text_chars;
1350
1351                         int idx = text.IndexOfAny (escaped);
1352                         if (idx >= 0) {
1353                                 char [] arr = text.ToCharArray ();
1354                                 WriteCheckedBuffer (arr, 0, idx);
1355                                 WriteEscapedBuffer (
1356                                         arr, idx, arr.Length - idx, isAttribute);
1357                         } else {
1358                                 WriteCheckedString (text);
1359                         }
1360                 }
1361
1362                 void WriteCheckedString (string s)
1363                 {
1364                         int i = XmlChar.IndexOfInvalid (s, true);
1365                         if (i >= 0) {
1366                                 char [] arr = s.ToCharArray ();
1367                                 writer.Write (arr, 0, i);
1368                                 WriteCheckedBuffer (arr, i, arr.Length - i);
1369                         } else {
1370                                 // no invalid character.
1371                                 writer.Write (s);
1372                         }
1373                 }
1374
1375                 void WriteCheckedBuffer (char [] text, int idx, int length)
1376                 {
1377                         int start = idx;
1378                         int end = idx + length;
1379                         while ((idx = XmlChar.IndexOfInvalid (text, start, length, true)) >= 0) {
1380                                 if (check_character_validity) // actually this is one time pass.
1381                                         throw ArgumentError (String.Format ("Input contains invalid character at {0} : &#x{1:X};", idx, (int) text [idx]));
1382                                 if (start < idx)
1383                                         writer.Write (text, start, idx - start);
1384                                 writer.Write ("&#x");
1385                                 writer.Write (((int) text [idx]).ToString (
1386                                         "X",
1387                                         CultureInfo.InvariantCulture));
1388                                 writer.Write (';');
1389                                 length -= idx - start + 1;
1390                                 start = idx + 1;
1391                         }
1392                         if (start < end)
1393                                 writer.Write (text, start, end - start);
1394                 }
1395
1396                 void WriteEscapedBuffer (char [] text, int index, int length,
1397                         bool isAttribute)
1398                 {
1399                         int start = index;
1400                         int end = index + length;
1401                         for (int i = start; i < end; i++) {
1402                                 switch (text [i]) {
1403                                 default:
1404                                         continue;
1405                                 case '&':
1406                                 case '<':
1407                                 case '>':
1408                                         if (start < i)
1409                                                 WriteCheckedBuffer (text, start, i - start);
1410                                         writer.Write ('&');
1411                                         switch (text [i]) {
1412                                         case '&': writer.Write ("amp;"); break;
1413                                         case '<': writer.Write ("lt;"); break;
1414                                         case '>': writer.Write ("gt;"); break;
1415                                         case '\'': writer.Write ("apos;"); break;
1416                                         case '"': writer.Write ("quot;"); break;
1417                                         }
1418                                         break;
1419                                 case '"':
1420                                 case '\'':
1421                                         if (isAttribute && text [i] == quote_char)
1422                                                 goto case '&';
1423                                         continue;
1424                                 case '\r':
1425                                         if (i + 1 < end && text [i] == '\n')
1426                                                 i++; // CRLF
1427                                         goto case '\n';
1428                                 case '\n':
1429                                         if (start < i)
1430                                                 WriteCheckedBuffer (text, start, i - start);
1431                                         if (isAttribute) {
1432                                                 writer.Write (text [i] == '\r' ?
1433                                                         "&#xD;" : "&#xA;");
1434                                                 break;
1435                                         }
1436                                         switch (newline_handling) {
1437                                         case NewLineHandling.Entitize:
1438                                                 writer.Write (text [i] == '\r' ?
1439                                                         "&#xD;" : "&#xA;");
1440                                                 break;
1441                                         case NewLineHandling.Replace:
1442                                                 writer.Write (newline);
1443                                                 break;
1444                                         default:
1445                                                 writer.Write (text [i]);
1446                                                 break;
1447                                         }
1448                                         break;
1449                                 }
1450                                 start = i + 1;
1451                         }
1452                         if (start < end)
1453                                 WriteCheckedBuffer (text, start, end - start);
1454                 }
1455
1456                 // Exceptions
1457
1458                 Exception ArgumentOutOfRangeError (string name)
1459                 {
1460 #if NET_2_0
1461                         state = WriteState.Error;
1462 #endif
1463                         return new ArgumentOutOfRangeException (name);
1464                 }
1465
1466                 Exception ArgumentError (string msg)
1467                 {
1468 #if NET_2_0
1469                         state = WriteState.Error;
1470 #endif
1471                         return new ArgumentException (msg);
1472                 }
1473
1474                 Exception InvalidOperation (string msg)
1475                 {
1476 #if NET_2_0
1477                         state = WriteState.Error;
1478 #endif
1479                         return new InvalidOperationException (msg);
1480                 }
1481
1482                 Exception StateError (string occured)
1483                 {
1484                         return InvalidOperation (String.Format ("This XmlWriter does not accept {0} at this state {1}.", occured, state));
1485                 }
1486         }
1487 }