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