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