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