27378cd78305fb77d0895b1ded11b627fb837d0a
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / XmlWellFormedWriter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlWellFormedWriter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 using System;
9 using System.IO;
10 using System.Text;
11 using System.Xml;
12 using System.Diagnostics;
13 using System.Collections;
14 using System.Globalization;
15 using System.Collections.Generic;
16
17 // OpenIssue : is it better to cache the current namespace decls for each elem
18 //  as the current code does, or should it just always walk the namespace stack?
19
20 namespace System.Xml {
21
22     internal partial class XmlWellFormedWriter : XmlWriter {
23         //
24         // Private types used by the XmlWellFormedWriter are defined in XmlWellFormedWriterHelpers.cs
25         //
26
27         //
28         // Fields
29         //
30         // underlying writer
31         XmlWriter writer;
32         XmlRawWriter rawWriter;  // writer as XmlRawWriter
33         IXmlNamespaceResolver predefinedNamespaces; // writer as IXmlNamespaceResolver
34
35         // namespace management
36         Namespace[] nsStack;
37         int nsTop;
38         Dictionary<string, int> nsHashtable;
39         bool useNsHashtable;
40
41         // element scoping
42         ElementScope[] elemScopeStack;
43         int elemTop;
44
45         // attribute tracking
46         AttrName[] attrStack;
47         int attrCount;
48         Dictionary<string, int> attrHashTable;
49
50         // special attribute caching (xmlns, xml:space, xml:lang)
51         SpecialAttribute specAttr = SpecialAttribute.No;
52         AttributeValueCache attrValueCache;
53         string curDeclPrefix;
54
55         // state machine
56         State[] stateTable;
57         State currentState;
58
59         // settings
60         bool checkCharacters;
61         bool omitDuplNamespaces;
62         bool writeEndDocumentOnClose;
63
64         // actual conformance level
65         ConformanceLevel conformanceLevel;
66
67         // flags
68         bool dtdWritten;
69         bool xmlDeclFollows;
70
71         // char type tables
72         XmlCharType xmlCharType = XmlCharType.Instance;
73
74         // hash randomizer
75         SecureStringHasher hasher;
76
77
78         //
79         // Constants
80         //
81         const int ElementStackInitialSize = 8;
82         const int NamespaceStackInitialSize = 8;
83         const int AttributeArrayInitialSize = 8;
84 #if DEBUG
85         const int MaxAttrDuplWalkCount = 2;
86         const int MaxNamespacesWalkCount = 3;
87 #else
88         const int MaxAttrDuplWalkCount = 14;
89         const int MaxNamespacesWalkCount = 16;
90 #endif
91
92         //
93         // State tables
94         //
95         enum State {
96             Start = 0,
97             TopLevel = 1,
98             Document = 2,
99             Element = 3,
100             Content = 4,
101             B64Content = 5,
102             B64Attribute = 6,
103             AfterRootEle = 7,
104             Attribute = 8,
105             SpecialAttr = 9,
106             EndDocument = 10,
107             RootLevelAttr = 11,
108             RootLevelSpecAttr = 12,
109             RootLevelB64Attr = 13,
110             AfterRootLevelAttr = 14,
111             Closed = 15,
112             Error = 16,
113
114             StartContent = 101,
115             StartContentEle = 102,
116             StartContentB64 = 103,
117             StartDoc = 104,
118             StartDocEle = 106,
119             EndAttrSEle = 107,
120             EndAttrEEle = 108,
121             EndAttrSCont = 109,
122             EndAttrSAttr = 111,
123             PostB64Cont = 112,
124             PostB64Attr = 113,
125             PostB64RootAttr = 114,
126             StartFragEle = 115,
127             StartFragCont = 116,
128             StartFragB64 = 117,
129             StartRootLevelAttr = 118,
130         }
131
132         enum Token {
133             StartDocument,
134             EndDocument,
135             PI,
136             Comment,
137             Dtd,
138             StartElement,
139             EndElement,
140             StartAttribute,
141             EndAttribute,
142             Text,
143             CData,
144             AtomicValue,
145             Base64,
146             RawData,
147             Whitespace,
148         }
149
150         internal static readonly string[] stateName = {
151             "Start",                     // State.Start                             
152             "TopLevel",                  // State.TopLevel       
153             "Document",                  // State.Document       
154             "Element Start Tag",         // State.Element        
155             "Element Content",           // State.Content        
156             "Element Content",           // State.B64Content        
157             "Attribute",                 // State.B64Attribute   
158             "EndRootElement",            // State.AfterRootEle   
159             "Attribute",                 // State.Attribute      
160             "Special Attribute",         // State.SpecialAttr
161             "End Document",              // State.EndDocument
162             "Root Level Attribute Value",           // State.RootLevelAttr
163             "Root Level Special Attribute Value",   // State.RootLevelSpecAttr
164             "Root Level Base64 Attribute Value",    // State.RootLevelB64Attr
165             "After Root Level Attribute",           // State.AfterRootLevelAttr
166             "Closed",                    // State.Closed
167             "Error",                     // State.Error
168         };
169
170         internal static readonly string[] tokenName = {
171             "StartDocument",            // Token.StartDocument
172             "EndDocument",              // Token.EndDocument
173             "PI",                       // Token.PI
174             "Comment",                  // Token.Comment
175             "DTD",                      // Token.Dtd
176             "StartElement",             // Token.StartElement
177             "EndElement",               // Token.EndElement
178             "StartAttribute",           // Token.StartAttribut
179             "EndAttribute",             // Token.EndAttribute
180             "Text",                     // Token.Text
181             "CDATA",                    // Token.CData
182             "Atomic value",             // Token.AtomicValue
183             "Base64",                   // Token.Base64
184             "RawData",                  // Token.RawData
185             "Whitespace",               // Token.Whitespace
186         };
187
188         private static WriteState[] state2WriteState = {    
189             WriteState.Start,       // State.Start       
190             WriteState.Prolog,      // State.TopLevel       
191             WriteState.Prolog,      // State.Document       
192             WriteState.Element,     // State.Element        
193             WriteState.Content,     // State.Content        
194             WriteState.Content,     // State.B64Content        
195             WriteState.Attribute,   // State.B64Attribute   
196             WriteState.Content,     // State.AfterRootEle   
197             WriteState.Attribute,   // State.Attribute      
198             WriteState.Attribute,   // State.SpecialAttr
199             WriteState.Content,     // State.EndDocument
200             WriteState.Attribute,   // State.RootLevelAttr
201             WriteState.Attribute,   // State.RootLevelSpecAttr
202             WriteState.Attribute,   // State.RootLevelB64Attr
203             WriteState.Attribute,   // State.AfterRootLevelAttr
204             WriteState.Closed,      // State.Closed
205             WriteState.Error,       // State.Error
206         };
207
208         private static readonly State[] StateTableDocument = {
209     //                         State.Start           State.TopLevel   State.Document     State.Element          State.Content     State.B64Content      State.B64Attribute   State.AfterRootEle    State.Attribute,      State.SpecialAttr,   State.EndDocument,  State.RootLevelAttr,      State.RootLevelSpecAttr,  State.RootLevelB64Attr   State.AfterRootLevelAttr, // 16
210     /* Token.StartDocument  */ State.Document,       State.Error,     State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.Error,         State.Error,          State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
211     /* Token.EndDocument    */ State.Error,          State.Error,     State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.Error,         State.EndDocument,    State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
212     /* Token.PI             */ State.StartDoc,       State.TopLevel,  State.Document,    State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
213     /* Token.Comment        */ State.StartDoc,       State.TopLevel,  State.Document,    State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
214     /* Token.Dtd            */ State.StartDoc,       State.TopLevel,  State.Document,    State.Error,           State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
215     /* Token.StartElement   */ State.StartDocEle,    State.Element,   State.Element,     State.StartContentEle, State.Element,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrSEle,    State.EndAttrSEle,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
216     /* Token.EndElement     */ State.Error,          State.Error,     State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrEEle,    State.EndAttrEEle,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
217     /* Token.StartAttribute */ State.Error,          State.Error,     State.Error,       State.Attribute,       State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrSAttr,   State.EndAttrSAttr,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
218     /* Token.EndAttribute   */ State.Error,          State.Error,     State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Element,        State.Element,       State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
219     /* Token.Text           */ State.Error,          State.Error,     State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Attribute,      State.SpecialAttr,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
220     /* Token.CData          */ State.Error,          State.Error,     State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
221     /* Token.AtomicValue    */ State.Error,          State.Error,     State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Attribute,      State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
222     /* Token.Base64         */ State.Error,          State.Error,     State.Error,       State.StartContentB64, State.B64Content, State.B64Content,     State.B64Attribute,  State.Error,          State.B64Attribute,   State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
223     /* Token.RawData        */ State.StartDoc,       State.Error,     State.Document,    State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.Attribute,      State.SpecialAttr,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error,
224     /* Token.Whitespace     */ State.StartDoc,       State.TopLevel,  State.Document,    State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.Attribute,      State.SpecialAttr,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error
225         };
226
227         private static readonly State[] StateTableAuto = {                                                                                                                                                                                                                                                                                                                                      
228     //                         State.Start           State.TopLevel       State.Document     State.Element          State.Content     State.B64Content      State.B64Attribute   State.AfterRootEle    State.Attribute,      State.SpecialAttr,   State.EndDocument,  State.RootLevelAttr,      State.RootLevelSpecAttr,  State.RootLevelB64Attr,  State.AfterRootLevelAttr  // 16
229     /* Token.StartDocument  */ State.Document,       State.Error,         State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.Error,         State.Error,          State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.StartDocument  */
230     /* Token.EndDocument    */ State.Error,          State.Error,         State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.Error,         State.EndDocument,    State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.EndDocument    */
231     /* Token.PI             */ State.TopLevel,       State.TopLevel,      State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.PI             */
232     /* Token.Comment        */ State.TopLevel,       State.TopLevel,      State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.Comment        */
233     /* Token.Dtd            */ State.StartDoc,       State.TopLevel,      State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Error,          State.Error,         State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.Dtd            */
234     /* Token.StartElement   */ State.StartFragEle,   State.Element,       State.Error,       State.StartContentEle, State.Element,    State.PostB64Cont,    State.PostB64Attr,   State.Element,        State.EndAttrSEle,    State.EndAttrSEle,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.StartElement   */
235     /* Token.EndElement     */ State.Error,          State.Error,         State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrEEle,    State.EndAttrEEle,   State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.EndElement     */
236     /* Token.StartAttribute */ State.RootLevelAttr,  State.Error,         State.Error,       State.Attribute,       State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.EndAttrSAttr,   State.EndAttrSAttr,  State.Error,        State.StartRootLevelAttr, State.StartRootLevelAttr, State.PostB64RootAttr,   State.RootLevelAttr,      State.Error, /* Token.StartAttribute */
237     /* Token.EndAttribute   */ State.Error,          State.Error,         State.Error,       State.Error,           State.Error,      State.PostB64Cont,    State.PostB64Attr,   State.Error,          State.Element,        State.Element,       State.Error,        State.AfterRootLevelAttr, State.AfterRootLevelAttr, State.PostB64RootAttr,   State.Error,              State.Error, /* Token.EndAttribute   */
238     /* Token.Text           */ State.StartFragCont,  State.StartFragCont, State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Content,        State.Attribute,      State.SpecialAttr,   State.Error,        State.RootLevelAttr,      State.RootLevelSpecAttr,  State.PostB64RootAttr,   State.Error,              State.Error, /* Token.Text           */
239     /* Token.CData          */ State.StartFragCont,  State.StartFragCont, State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Content,        State.EndAttrSCont,   State.EndAttrSCont,  State.Error,        State.Error,              State.Error,              State.Error,             State.Error,              State.Error, /* Token.CData          */
240     /* Token.AtomicValue    */ State.StartFragCont,  State.StartFragCont, State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Content,        State.Attribute,      State.Error,         State.Error,        State.RootLevelAttr,      State.Error,              State.PostB64RootAttr,   State.Error,              State.Error, /* Token.AtomicValue    */
241     /* Token.Base64         */ State.StartFragB64,   State.StartFragB64,  State.Error,       State.StartContentB64, State.B64Content, State.B64Content,     State.B64Attribute,  State.B64Content,     State.B64Attribute,   State.Error,         State.Error,        State.RootLevelB64Attr,   State.Error,              State.RootLevelB64Attr,  State.Error,              State.Error, /* Token.Base64         */
242     /* Token.RawData        */ State.StartFragCont,  State.TopLevel,      State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.Content,        State.Attribute,      State.SpecialAttr,   State.Error,        State.RootLevelAttr,      State.RootLevelSpecAttr,  State.PostB64RootAttr,   State.AfterRootLevelAttr, State.Error, /* Token.RawData        */
243     /* Token.Whitespace     */ State.TopLevel,       State.TopLevel,      State.Error,       State.StartContent,    State.Content,    State.PostB64Cont,    State.PostB64Attr,   State.AfterRootEle,   State.Attribute,      State.SpecialAttr,   State.Error,        State.RootLevelAttr,      State.RootLevelSpecAttr,  State.PostB64RootAttr,   State.AfterRootLevelAttr, State.Error, /* Token.Whitespace     */
244         };
245
246         //
247         // Constructor & finalizer
248         //
249         internal XmlWellFormedWriter(XmlWriter writer, XmlWriterSettings settings) {
250             Debug.Assert(writer != null);
251             Debug.Assert(settings != null);
252             Debug.Assert(MaxNamespacesWalkCount <= 3);
253
254             this.writer = writer;
255
256             rawWriter = writer as XmlRawWriter;
257             predefinedNamespaces = writer as IXmlNamespaceResolver;
258             if (rawWriter != null) {
259                 rawWriter.NamespaceResolver = new NamespaceResolverProxy(this);
260             }
261
262             checkCharacters = settings.CheckCharacters;
263             omitDuplNamespaces = (settings.NamespaceHandling & NamespaceHandling.OmitDuplicates) != 0;
264             writeEndDocumentOnClose = settings.WriteEndDocumentOnClose;
265
266             conformanceLevel = settings.ConformanceLevel;
267             stateTable = (conformanceLevel == ConformanceLevel.Document) ? StateTableDocument : StateTableAuto;
268
269             currentState = State.Start;
270
271             nsStack = new Namespace[NamespaceStackInitialSize];
272             nsStack[0].Set("xmlns", XmlReservedNs.NsXmlNs, NamespaceKind.Special);
273             nsStack[1].Set("xml", XmlReservedNs.NsXml, NamespaceKind.Special);
274             if (predefinedNamespaces == null) {
275                 nsStack[2].Set(string.Empty, string.Empty, NamespaceKind.Implied);
276             }
277             else {
278                 string defaultNs = predefinedNamespaces.LookupNamespace(string.Empty);
279                 nsStack[2].Set(string.Empty, (defaultNs == null ? string.Empty : defaultNs), NamespaceKind.Implied);
280             }
281             nsTop = 2;
282
283             elemScopeStack = new ElementScope[ElementStackInitialSize];
284             elemScopeStack[0].Set(string.Empty, string.Empty, string.Empty, nsTop);
285             elemScopeStack[0].xmlSpace = XmlSpace.None;
286             elemScopeStack[0].xmlLang = null;
287             elemTop = 0;
288
289             attrStack = new AttrName[AttributeArrayInitialSize];
290
291             hasher = new SecureStringHasher();
292         }
293
294         //
295         // XmlWriter implementation
296         //
297         public override WriteState WriteState {
298             get {
299                 if ((int)currentState <= (int)State.Error) {
300                     return state2WriteState[(int)currentState];
301                 }
302                 else {
303                     Debug.Assert(false, "Expected currentState <= State.Error ");
304                     return WriteState.Error;
305                 }
306             }
307         }
308
309         public override XmlWriterSettings Settings {
310             get {
311                 XmlWriterSettings settings = writer.Settings;
312                 settings.ReadOnly = false;
313                 settings.ConformanceLevel = conformanceLevel;
314                 if (omitDuplNamespaces) {
315                     settings.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
316                 }
317                 settings.WriteEndDocumentOnClose = writeEndDocumentOnClose;
318                 settings.ReadOnly = true;
319                 return settings;
320             }
321         }
322
323         public override void WriteStartDocument() {
324             WriteStartDocumentImpl(XmlStandalone.Omit);
325         }
326
327         public override void WriteStartDocument(bool standalone) {
328             WriteStartDocumentImpl(standalone ? XmlStandalone.Yes : XmlStandalone.No);
329         }
330
331         public override void WriteEndDocument() {
332             try {
333                 // auto-close all elements
334                 while (elemTop > 0) {
335                     WriteEndElement();
336                 }
337                 State prevState = currentState;
338                 AdvanceState(Token.EndDocument);
339
340                 if (prevState != State.AfterRootEle) {
341                     throw new ArgumentException(Res.GetString(Res.Xml_NoRoot));
342                 }
343                 if (rawWriter == null) {
344                     writer.WriteEndDocument();
345                 }
346             }
347             catch {
348                 currentState = State.Error;
349                 throw;
350             }
351         }
352
353         public override void WriteDocType(string name, string pubid, string sysid, string subset) {
354             try {
355                 if (name == null || name.Length == 0) {
356                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
357                 }
358                 XmlConvert.VerifyQName(name, ExceptionType.XmlException);
359
360                 if (conformanceLevel == ConformanceLevel.Fragment) {
361                     throw new InvalidOperationException(Res.GetString(Res.Xml_DtdNotAllowedInFragment));
362                 }
363
364                 AdvanceState(Token.Dtd);
365                 if (dtdWritten) {
366                     currentState = State.Error;
367                     throw new InvalidOperationException(Res.GetString(Res.Xml_DtdAlreadyWritten));
368                 }
369
370                 if (conformanceLevel == ConformanceLevel.Auto) {
371                     conformanceLevel = ConformanceLevel.Document;
372                     stateTable = StateTableDocument;
373                 }
374
375                 int i;
376
377                 // check characters
378                 if (checkCharacters) {
379                     if (pubid != null) {
380                         if ((i = xmlCharType.IsPublicId(pubid)) >= 0) {
381                             throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(pubid, i)), "pubid");
382                         }
383                     }
384                     if (sysid != null) {
385                         if ((i = xmlCharType.IsOnlyCharData(sysid)) >= 0) {
386                             throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(sysid, i)), "sysid");
387                         }
388                     }
389                     if (subset != null) {
390                         if ((i = xmlCharType.IsOnlyCharData(subset)) >= 0) {
391                             throw new ArgumentException(Res.GetString(Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(subset, i)), "subset");
392                         }
393                     }
394                 }
395
396                 // write doctype
397                 writer.WriteDocType(name, pubid, sysid, subset);
398                 dtdWritten = true;
399             }
400             catch {
401                 currentState = State.Error;
402                 throw;
403             }
404         }
405
406         public override void WriteStartElement(string prefix, string localName, string ns) {
407             try {
408                 // check local name
409                 if (localName == null || localName.Length == 0) {
410                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
411                 }
412                 CheckNCName(localName);
413
414                 AdvanceState(Token.StartElement);
415
416                 // lookup prefix / namespace  
417                 if (prefix == null) {
418                     if (ns != null) {
419                         prefix = LookupPrefix(ns);
420                     }
421                     if (prefix == null) {
422                         prefix = string.Empty;
423                     }
424                 }
425                 else if (prefix.Length > 0) {
426                     CheckNCName(prefix);
427                     if (ns == null) {
428                         ns = LookupNamespace(prefix);
429                     }
430                     if (ns == null || (ns != null && ns.Length == 0)) {
431                         throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
432                     }
433                 }
434                 if (ns == null) {
435                     ns = LookupNamespace(prefix);
436                     if (ns == null) {
437                         Debug.Assert(prefix.Length == 0);
438                         ns = string.Empty;
439                     }
440                 }
441
442                 if (elemTop == 0 && rawWriter != null) {
443                     // notify the underlying raw writer about the root level element
444                     rawWriter.OnRootElement(conformanceLevel);
445                 }
446
447                 // write start tag
448                 writer.WriteStartElement(prefix, localName, ns);
449
450                 // push element on stack and add/check namespace
451                 int top = ++elemTop;
452                 if (top == elemScopeStack.Length) {
453                     ElementScope[] newStack = new ElementScope[top * 2];
454                     Array.Copy(elemScopeStack, newStack, top);
455                     elemScopeStack = newStack;
456                 }
457                 elemScopeStack[top].Set(prefix, localName, ns, nsTop);
458
459                 PushNamespaceImplicit(prefix, ns);
460
461                 if (attrCount >= MaxAttrDuplWalkCount) {
462                     attrHashTable.Clear();
463                 }
464                 attrCount = 0;
465
466             }
467             catch {
468                 currentState = State.Error;
469                 throw;
470             }
471         }
472
473
474         public override void WriteEndElement() {
475             try {
476                 AdvanceState(Token.EndElement);
477
478                 int top = elemTop;
479                 if (top == 0) {
480                     throw new XmlException(Res.Xml_NoStartTag, string.Empty);
481                 }
482
483                 // write end tag
484                 if (rawWriter != null) {
485                     elemScopeStack[top].WriteEndElement(rawWriter);
486                 }
487                 else {
488                     writer.WriteEndElement();
489                 }
490
491                 // pop namespaces
492                 int prevNsTop = elemScopeStack[top].prevNSTop;
493                 if (useNsHashtable && prevNsTop < nsTop) {
494                     PopNamespaces(prevNsTop + 1, nsTop);
495                 }
496                 nsTop = prevNsTop;
497                 elemTop = --top;
498
499                 // check "one root element" condition for ConformanceLevel.Document
500                 if (top == 0) {
501                     if (conformanceLevel == ConformanceLevel.Document) {
502                         currentState = State.AfterRootEle;
503                     }
504                     else {
505                         currentState = State.TopLevel;
506                     }
507                 }
508             }
509             catch {
510                 currentState = State.Error;
511                 throw;
512             }
513         }
514
515         public override void WriteFullEndElement() {
516             try {
517                 AdvanceState(Token.EndElement);
518
519                 int top = elemTop;
520                 if (top == 0) {
521                     throw new XmlException(Res.Xml_NoStartTag, string.Empty);
522                 }
523
524                 // write end tag
525                 if (rawWriter != null) {
526                     elemScopeStack[top].WriteFullEndElement(rawWriter);
527                 }
528                 else {
529                     writer.WriteFullEndElement();
530                 }
531
532                 // pop namespaces
533                 int prevNsTop = elemScopeStack[top].prevNSTop;
534                 if (useNsHashtable && prevNsTop < nsTop) {
535                     PopNamespaces(prevNsTop + 1, nsTop);
536                 }
537                 nsTop = prevNsTop;
538                 elemTop = --top;
539
540                 // check "one root element" condition for ConformanceLevel.Document
541                 if (top == 0) {
542                     if (conformanceLevel == ConformanceLevel.Document) {
543                         currentState = State.AfterRootEle;
544                     }
545                     else {
546                         currentState = State.TopLevel;
547                     }
548                 }
549             }
550             catch {
551                 currentState = State.Error;
552                 throw;
553             }
554         }
555
556         public override void WriteStartAttribute(string prefix, string localName, string namespaceName) {
557             try {
558                 // check local name
559                 if (localName == null || localName.Length == 0) {
560                     if (prefix == "xmlns") {
561                         localName = "xmlns";
562                         prefix = string.Empty;
563                     }
564                     else {
565                         throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
566                     }
567                 }
568                 CheckNCName(localName);
569
570                 AdvanceState(Token.StartAttribute);
571
572                 // lookup prefix / namespace  
573                 if (prefix == null) {
574                     if (namespaceName != null) {
575                         // special case prefix=null/localname=xmlns
576                         if (!(localName == "xmlns" && namespaceName == XmlReservedNs.NsXmlNs))
577                             prefix = LookupPrefix(namespaceName);
578                     }
579                     if (prefix == null) {
580                         prefix = string.Empty;
581                     }
582                 }
583                 if (namespaceName == null) {
584                     if (prefix != null && prefix.Length > 0) {
585                         namespaceName = LookupNamespace(prefix);
586                     }
587                     if (namespaceName == null) {
588                         namespaceName = string.Empty;
589                     }
590                 }
591
592                 if (prefix.Length == 0) {
593                     if (localName[0] == 'x' && localName == "xmlns") {
594                         if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXmlNs) {
595                             throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
596                         }
597                         curDeclPrefix = String.Empty;
598                         SetSpecialAttribute(SpecialAttribute.DefaultXmlns);
599                         goto SkipPushAndWrite;
600                     }
601                     else if (namespaceName.Length > 0) {
602                         prefix = LookupPrefix(namespaceName);
603                         if (prefix == null || prefix.Length == 0) {
604                             prefix = GeneratePrefix();
605                         }
606                     }
607                 }
608                 else {
609                     if (prefix[0] == 'x') {
610                         if (prefix == "xmlns") {
611                             if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXmlNs) {
612                                 throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
613                             }
614                             curDeclPrefix = localName;
615                             SetSpecialAttribute(SpecialAttribute.PrefixedXmlns);
616                             goto SkipPushAndWrite;
617                         }
618                         else if (prefix == "xml") {
619                             if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXml) {
620                                 throw new ArgumentException(Res.GetString(Res.Xml_XmlPrefix));
621                             }
622                             switch (localName) {
623                                 case "space":
624                                     SetSpecialAttribute(SpecialAttribute.XmlSpace);
625                                     goto SkipPushAndWrite;
626                                 case "lang":
627                                     SetSpecialAttribute(SpecialAttribute.XmlLang);
628                                     goto SkipPushAndWrite;
629                             }
630                         }
631                     }
632
633                     CheckNCName(prefix);
634
635                     if (namespaceName.Length == 0) {
636                         // attributes cannot have default namespace
637                         prefix = string.Empty;
638                     }
639                     else {
640                         string definedNs = LookupLocalNamespace(prefix);
641                         if (definedNs != null && definedNs != namespaceName) {
642                             prefix = GeneratePrefix();
643                         }
644                     }
645                 }
646
647                 if (prefix.Length != 0) {
648                     PushNamespaceImplicit(prefix, namespaceName);
649                 }
650
651             SkipPushAndWrite:
652
653                 // add attribute to the list and check for duplicates
654                 AddAttribute( prefix, localName, namespaceName );
655
656                 if (specAttr == SpecialAttribute.No) {
657                     // write attribute name
658                     writer.WriteStartAttribute( prefix, localName, namespaceName );
659                 }
660             }
661             catch {
662                 currentState = State.Error;
663                 throw;
664             }
665         }
666
667         public override void WriteEndAttribute() {
668             try {
669                 AdvanceState(Token.EndAttribute);
670
671                 if (specAttr != SpecialAttribute.No) {
672                     string value;
673
674                     switch (specAttr) {
675                         case SpecialAttribute.DefaultXmlns:
676                             value = attrValueCache.StringValue;
677                             if (PushNamespaceExplicit(string.Empty, value)) { // returns true if the namespace declaration should be written out
678                                 if (rawWriter != null) {
679                                     if (rawWriter.SupportsNamespaceDeclarationInChunks) {
680                                         rawWriter.WriteStartNamespaceDeclaration(string.Empty);
681                                         attrValueCache.Replay(rawWriter);
682                                         rawWriter.WriteEndNamespaceDeclaration();
683                                     }
684                                     else {
685                                         rawWriter.WriteNamespaceDeclaration(string.Empty, value);
686                                     }
687                                 }
688                                 else {
689                                     writer.WriteStartAttribute(string.Empty, "xmlns", XmlReservedNs.NsXmlNs);
690                                     attrValueCache.Replay(writer);
691                                     writer.WriteEndAttribute();
692                                 }
693                             }
694                             curDeclPrefix = null;
695                             break;
696                         case SpecialAttribute.PrefixedXmlns:
697                             value = attrValueCache.StringValue;
698                             if (value.Length == 0) {
699                                 throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
700                             }
701                             if (value == XmlReservedNs.NsXmlNs || (value == XmlReservedNs.NsXml && curDeclPrefix != "xml")) {
702                                 throw new ArgumentException(Res.GetString(Res.Xml_CanNotBindToReservedNamespace));
703                             }
704                             if (PushNamespaceExplicit(curDeclPrefix, value)) { // returns true if the namespace declaration should be written out
705                                 if (rawWriter != null) {
706                                     if (rawWriter.SupportsNamespaceDeclarationInChunks) {
707                                         rawWriter.WriteStartNamespaceDeclaration(curDeclPrefix);
708                                         attrValueCache.Replay(rawWriter);
709                                         rawWriter.WriteEndNamespaceDeclaration();
710                                     }
711                                     else {
712                                         rawWriter.WriteNamespaceDeclaration(curDeclPrefix, value);
713                                     }
714                                 }
715                                 else {
716                                     writer.WriteStartAttribute("xmlns", curDeclPrefix, XmlReservedNs.NsXmlNs);
717                                     attrValueCache.Replay(writer);
718                                     writer.WriteEndAttribute();
719                                 }
720                             }
721                             curDeclPrefix = null;
722                             break;
723                         case SpecialAttribute.XmlSpace:
724                             attrValueCache.Trim();
725                             value = attrValueCache.StringValue;
726
727                             if (value == "default") {
728                                 elemScopeStack[elemTop].xmlSpace = XmlSpace.Default;
729                             }
730                             else if (value == "preserve") {
731                                 elemScopeStack[elemTop].xmlSpace = XmlSpace.Preserve;
732                             }
733                             else {
734                                 throw new ArgumentException(Res.GetString(Res.Xml_InvalidXmlSpace, value));
735                             }
736                             writer.WriteStartAttribute("xml", "space", XmlReservedNs.NsXml);
737                             attrValueCache.Replay(writer);
738                             writer.WriteEndAttribute();
739                             break;
740                         case SpecialAttribute.XmlLang:
741                             value = attrValueCache.StringValue;
742                             elemScopeStack[elemTop].xmlLang = value;
743                             writer.WriteStartAttribute("xml", "lang", XmlReservedNs.NsXml);
744                             attrValueCache.Replay(writer);
745                             writer.WriteEndAttribute();
746                             break;
747                     }
748                     specAttr = SpecialAttribute.No;
749                     attrValueCache.Clear();
750                 }
751                 else {
752                     writer.WriteEndAttribute();
753                 }
754             }
755             catch {
756                 currentState = State.Error;
757                 throw;
758             }
759         }
760
761         public override void WriteCData(string text) {
762             try {
763                 if (text == null) {
764                     text = string.Empty;
765                 }
766                 AdvanceState(Token.CData);
767                 writer.WriteCData(text);
768             }
769             catch {
770                 currentState = State.Error;
771                 throw;
772             }
773         }
774
775         public override void WriteComment(string text) {
776             try {
777                 if (text == null) {
778                     text = string.Empty;
779                 }
780                 AdvanceState(Token.Comment);
781                 writer.WriteComment(text);
782             }
783             catch {
784                 currentState = State.Error;
785                 throw;
786             }
787         }
788
789         public override void WriteProcessingInstruction(string name, string text) {
790             try {
791                 // check name
792                 if (name == null || name.Length == 0) {
793                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
794                 }
795                 CheckNCName(name);
796
797                 // check text
798                 if (text == null) {
799                     text = string.Empty;
800                 }
801
802                 // xml declaration is a special case (not a processing instruction, but we allow WriteProcessingInstruction as a convenience)
803                 if (name.Length == 3 && string.Compare(name, "xml", StringComparison.OrdinalIgnoreCase) == 0) {
804                     if (currentState != State.Start) {
805                         throw new ArgumentException(Res.GetString(conformanceLevel == ConformanceLevel.Document ? Res.Xml_DupXmlDecl : Res.Xml_CannotWriteXmlDecl));
806                     }
807
808                     xmlDeclFollows = true;
809                     AdvanceState(Token.PI);
810
811                     if (rawWriter != null) {
812                         // Translate PI into an xml declaration
813                         rawWriter.WriteXmlDeclaration(text);
814                     }
815                     else {
816                         writer.WriteProcessingInstruction(name, text);
817                     }
818                 }
819                 else {
820                     AdvanceState(Token.PI);
821                     writer.WriteProcessingInstruction(name, text);
822                 }
823             }
824             catch {
825                 currentState = State.Error;
826                 throw;
827             }
828         }
829
830         public override void WriteEntityRef(string name) {
831             try {
832                 // check name
833                 if (name == null || name.Length == 0) {
834                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
835                 }
836                 CheckNCName(name);
837
838                 AdvanceState(Token.Text);
839                 if (SaveAttrValue) {
840                     attrValueCache.WriteEntityRef(name);
841                 }
842                 else {
843                     writer.WriteEntityRef(name);
844                 }
845             }
846             catch {
847                 currentState = State.Error;
848                 throw;
849             }
850         }
851
852         public override void WriteCharEntity(char ch) {
853             try {
854                 if (Char.IsSurrogate(ch)) {
855                     throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
856                 }
857
858                 AdvanceState(Token.Text);
859                 if (SaveAttrValue) {
860                     attrValueCache.WriteCharEntity(ch);
861                 }
862                 else {
863                     writer.WriteCharEntity(ch);
864                 }
865             }
866             catch {
867                 currentState = State.Error;
868                 throw;
869             }
870         }
871
872         public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
873             try {
874                 if (!Char.IsSurrogatePair(highChar, lowChar)) {
875                     throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
876                 }
877
878                 AdvanceState(Token.Text);
879                 if (SaveAttrValue) {
880                     attrValueCache.WriteSurrogateCharEntity(lowChar, highChar);
881                 }
882                 else {
883                     writer.WriteSurrogateCharEntity(lowChar, highChar);
884                 }
885             }
886             catch {
887                 currentState = State.Error;
888                 throw;
889             }
890         }
891
892         public override void WriteWhitespace(string ws) {
893             try {
894                 if (ws == null) {
895                     ws = string.Empty;
896                 }
897                 if (!XmlCharType.Instance.IsOnlyWhitespace(ws)) {
898                     throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace));
899                 }
900
901                 AdvanceState(Token.Whitespace);
902                 if (SaveAttrValue) {
903                     attrValueCache.WriteWhitespace(ws);
904                 }
905                 else {
906                     writer.WriteWhitespace(ws);
907                 }
908             }
909             catch {
910                 currentState = State.Error;
911                 throw;
912             }
913         }
914
915         public override void WriteString(string text) {
916             try {
917                 if (text == null) {
918                     return;
919                 }
920
921                 AdvanceState(Token.Text);
922                 if (SaveAttrValue) {
923                     attrValueCache.WriteString(text);
924                 }
925                 else {
926                     writer.WriteString(text);
927                 }
928             }
929             catch {
930                 currentState = State.Error;
931                 throw;
932             }
933         }
934
935         public override void WriteChars(char[] buffer, int index, int count) {
936             try {
937                 if (buffer == null) {
938                     throw new ArgumentNullException("buffer");
939                 }
940                 if (index < 0) {
941                     throw new ArgumentOutOfRangeException("index");
942                 }
943                 if (count < 0) {
944                     throw new ArgumentOutOfRangeException("count");
945                 }
946                 if (count > buffer.Length - index) {
947                     throw new ArgumentOutOfRangeException("count");
948                 }
949
950                 AdvanceState(Token.Text);
951                 if (SaveAttrValue) {
952                     attrValueCache.WriteChars(buffer, index, count);
953                 }
954                 else {
955                     writer.WriteChars(buffer, index, count);
956                 }
957             }
958             catch {
959                 currentState = State.Error;
960                 throw;
961             }
962         }
963
964         public override void WriteRaw(char[] buffer, int index, int count) {
965             try {
966                 if (buffer == null) {
967                     throw new ArgumentNullException("buffer");
968                 }
969                 if (index < 0) {
970                     throw new ArgumentOutOfRangeException("index");
971                 }
972                 if (count < 0) {
973                     throw new ArgumentOutOfRangeException("count");
974                 }
975                 if (count > buffer.Length - index) {
976                     throw new ArgumentOutOfRangeException("count");
977                 }
978
979                 AdvanceState(Token.RawData);
980                 if (SaveAttrValue) {
981                     attrValueCache.WriteRaw(buffer, index, count);
982                 }
983                 else {
984                     writer.WriteRaw(buffer, index, count);
985                 }
986             }
987             catch {
988                 currentState = State.Error;
989                 throw;
990             }
991         }
992
993         public override void WriteRaw(string data) {
994             try {
995                 if (data == null) {
996                     return;
997                 }
998
999                 AdvanceState(Token.RawData);
1000                 if (SaveAttrValue) {
1001                     attrValueCache.WriteRaw(data);
1002                 }
1003                 else {
1004                     writer.WriteRaw(data);
1005                 }
1006             }
1007             catch {
1008                 currentState = State.Error;
1009                 throw;
1010             }
1011         }
1012
1013         public override void WriteBase64(byte[] buffer, int index, int count) {
1014             try {
1015                 if (buffer == null) {
1016                     throw new ArgumentNullException("buffer");
1017                 }
1018                 if (index < 0) {
1019                     throw new ArgumentOutOfRangeException("index");
1020                 }
1021                 if (count < 0) {
1022                     throw new ArgumentOutOfRangeException("count");
1023                 }
1024                 if (count > buffer.Length - index) {
1025                     throw new ArgumentOutOfRangeException("count");
1026                 }
1027
1028                 AdvanceState(Token.Base64);
1029                 writer.WriteBase64(buffer, index, count);
1030             }
1031             catch {
1032                 currentState = State.Error;
1033                 throw;
1034             }
1035         }
1036
1037         public override void Close() {
1038             if (currentState != State.Closed) {
1039                 try {
1040                     if (writeEndDocumentOnClose) {
1041                         while (currentState != State.Error && elemTop > 0) {
1042                             WriteEndElement();
1043                         }
1044                     }
1045                     else {
1046                         if (currentState != State.Error && elemTop > 0) {
1047                             //finish the start element tag '>'
1048                             try {
1049                                 AdvanceState(Token.EndElement);
1050                             }
1051                             catch {
1052                                 currentState = State.Error;
1053                                 throw;
1054                             }
1055                         }
1056                     }
1057
1058                     if (InBase64 && rawWriter != null) {
1059                         rawWriter.WriteEndBase64();
1060                     }
1061
1062                     writer.Flush();
1063                 }
1064                 finally {
1065                     try {
1066                         if (rawWriter != null) {
1067                             rawWriter.Close(WriteState);
1068                         }
1069                         else {
1070                             writer.Close();
1071                         }
1072                     }
1073                     finally {
1074                         currentState = State.Closed;
1075                     }
1076                 }
1077             }
1078         }
1079
1080         public override void Flush() {
1081             try {
1082                 writer.Flush();
1083             }
1084             catch {
1085                 currentState = State.Error;
1086                 throw;
1087             }
1088         }
1089
1090         public override string LookupPrefix(string ns) {
1091             try {
1092                 if (ns == null) {
1093                     throw new ArgumentNullException("ns");
1094                 }
1095                 for (int i = nsTop; i >= 0; i--) {
1096                     if (nsStack[i].namespaceUri == ns) {
1097                         string prefix = nsStack[i].prefix;
1098                         for (i++; i <= nsTop; i++) {
1099                             if (nsStack[i].prefix == prefix) {
1100                                 return null;
1101                             }
1102                         }
1103                         return prefix;
1104                     }
1105                 }
1106                 return (predefinedNamespaces != null) ? predefinedNamespaces.LookupPrefix(ns) : null;
1107             }
1108             catch {
1109                 currentState = State.Error;
1110                 throw;
1111             }
1112         }
1113
1114         public override XmlSpace XmlSpace {
1115             get {
1116                 int i;
1117                 for (i = elemTop; i >= 0 && elemScopeStack[i].xmlSpace == (System.Xml.XmlSpace)(int)-1; i--) ;
1118                 Debug.Assert(i >= 0);
1119                 return elemScopeStack[i].xmlSpace;
1120             }
1121         }
1122
1123         public override string XmlLang {
1124             get {
1125                 int i;
1126                 for (i = elemTop; i > 0 && elemScopeStack[i].xmlLang == null; i--) ;
1127                 Debug.Assert(i >= 0);
1128                 return elemScopeStack[i].xmlLang;
1129             }
1130         }
1131
1132         public override void WriteQualifiedName(string localName, string ns) {
1133             try {
1134                 if (localName == null || localName.Length == 0) {
1135                     throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
1136                 }
1137                 CheckNCName(localName);
1138
1139                 AdvanceState(Token.Text);
1140                 string prefix = String.Empty;
1141                 if (ns != null && ns.Length != 0) {
1142                     prefix = LookupPrefix(ns);
1143                     if (prefix == null) {
1144                         if (currentState != State.Attribute) {
1145                             throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns));
1146                         }
1147                         prefix = GeneratePrefix();
1148                         PushNamespaceImplicit(prefix, ns);
1149                     }
1150                 }
1151                 // if this is a special attribute, then just convert this to text
1152                 // otherwise delegate to raw-writer
1153                 if (SaveAttrValue || rawWriter == null) {
1154                     if (prefix.Length != 0) {
1155                         WriteString(prefix);
1156                         WriteString(":");
1157                     }
1158                     WriteString(localName);
1159                 }
1160                 else {
1161                     rawWriter.WriteQualifiedName(prefix, localName, ns);
1162                 }
1163             }
1164             catch {
1165                 currentState = State.Error;
1166                 throw;
1167             }
1168         }
1169
1170         public override void WriteValue(bool value) {
1171             try {
1172                 AdvanceState(Token.AtomicValue);
1173                 writer.WriteValue(value);
1174             }
1175             catch {
1176                 currentState = State.Error;
1177                 throw;
1178             }
1179         }
1180
1181         public override void WriteValue(DateTime value) {
1182             try {
1183                 AdvanceState(Token.AtomicValue);
1184                 writer.WriteValue(value);
1185             }
1186             catch {
1187                 currentState = State.Error;
1188                 throw;
1189             }
1190         }
1191
1192         public override void WriteValue(DateTimeOffset value) {
1193             try {
1194                 AdvanceState(Token.AtomicValue);
1195                 writer.WriteValue(value);
1196             }
1197             catch {
1198                 currentState = State.Error;
1199                 throw;
1200             }
1201         }
1202
1203         public override void WriteValue(double value) {
1204             try {
1205                 AdvanceState(Token.AtomicValue);
1206                 writer.WriteValue(value);
1207             }
1208             catch {
1209                 currentState = State.Error;
1210                 throw;
1211             }
1212         }
1213
1214         public override void WriteValue(float value) {
1215             try {
1216                 AdvanceState(Token.AtomicValue);
1217                 writer.WriteValue(value);
1218             }
1219             catch {
1220                 currentState = State.Error;
1221                 throw;
1222             }
1223         }
1224
1225         public override void WriteValue(decimal value) {
1226             try {
1227                 AdvanceState(Token.AtomicValue);
1228                 writer.WriteValue(value);
1229             }
1230             catch {
1231                 currentState = State.Error;
1232                 throw;
1233             }
1234         }
1235
1236         public override void WriteValue(int value) {
1237             try {
1238                 AdvanceState(Token.AtomicValue);
1239                 writer.WriteValue(value);
1240             }
1241             catch {
1242                 currentState = State.Error;
1243                 throw;
1244             }
1245         }
1246
1247         public override void WriteValue(long value) {
1248             try {
1249                 AdvanceState(Token.AtomicValue);
1250                 writer.WriteValue(value);
1251             }
1252             catch {
1253                 currentState = State.Error;
1254                 throw;
1255             }
1256         }
1257
1258         public override void WriteValue(string value) {
1259             try {
1260                 if (value == null) {
1261                     return;
1262                 }
1263                 if (SaveAttrValue) {
1264                     AdvanceState(Token.Text);
1265                     attrValueCache.WriteValue(value);
1266                 }
1267                 else {
1268                     AdvanceState(Token.AtomicValue);
1269                     writer.WriteValue(value);
1270                 }
1271             }
1272             catch {
1273                 currentState = State.Error;
1274                 throw;
1275             }
1276         }
1277
1278         public override void WriteValue(object value) {
1279             try {
1280                 if (SaveAttrValue && value is string) {
1281                     AdvanceState(Token.Text);
1282                     attrValueCache.WriteValue((string)value);
1283                 }
1284                 else {
1285                     AdvanceState(Token.AtomicValue);
1286                     writer.WriteValue(value);
1287                 }
1288             }
1289             catch {
1290                 currentState = State.Error;
1291                 throw;
1292             }
1293         }
1294
1295         public override void WriteBinHex(byte[] buffer, int index, int count) {
1296             if (IsClosedOrErrorState) {
1297                 throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
1298             }
1299             try {
1300                 AdvanceState(Token.Text);
1301                 base.WriteBinHex(buffer, index, count);
1302             }
1303             catch {
1304                 currentState = State.Error;
1305                 throw;
1306             }
1307         }
1308
1309         //
1310         // Internal methods
1311         //
1312 #if !SILVERLIGHT
1313         internal XmlWriter InnerWriter {
1314             get {
1315                 return this.writer;
1316             }
1317         }
1318
1319         internal XmlRawWriter RawWriter {
1320             get {
1321                 return rawWriter;
1322             }
1323         }
1324 #endif
1325
1326         //
1327         // Private methods
1328         //
1329         private bool SaveAttrValue {
1330             get {
1331                 return specAttr != SpecialAttribute.No;
1332             }
1333         }
1334
1335         private bool InBase64 {
1336             get {
1337                 return (currentState == State.B64Content || currentState == State.B64Attribute || currentState == State.RootLevelB64Attr);
1338             }
1339         }
1340
1341         private void SetSpecialAttribute(SpecialAttribute special) {
1342             specAttr = special;
1343             if (State.Attribute == currentState)
1344                 currentState = State.SpecialAttr;
1345             else if (State.RootLevelAttr == currentState)
1346                 currentState = State.RootLevelSpecAttr;
1347             else
1348                 Debug.Assert(false, "State.Attribute == currentState || State.RootLevelAttr == currentState");
1349
1350             if (attrValueCache == null) {
1351                 attrValueCache = new AttributeValueCache();
1352             }
1353         }
1354
1355         private void WriteStartDocumentImpl(XmlStandalone standalone) {
1356             try {
1357                 AdvanceState(Token.StartDocument);
1358
1359                 if (conformanceLevel == ConformanceLevel.Auto) {
1360                     conformanceLevel = ConformanceLevel.Document;
1361                     stateTable = StateTableDocument;
1362                 }
1363                 else if (conformanceLevel == ConformanceLevel.Fragment) {
1364                     throw new InvalidOperationException(Res.GetString(Res.Xml_CannotStartDocumentOnFragment));
1365                 }
1366
1367                 if (rawWriter != null) {
1368                     if (!xmlDeclFollows) {
1369                         rawWriter.WriteXmlDeclaration(standalone);
1370                     }
1371                 }
1372                 else {
1373                     // We do not pass the standalone value here - Dev10 
1374                     writer.WriteStartDocument();
1375                 }
1376             }
1377             catch {
1378                 currentState = State.Error;
1379                 throw;
1380             }
1381         }
1382
1383         private void StartFragment() {
1384             conformanceLevel = ConformanceLevel.Fragment;
1385             Debug.Assert(stateTable == StateTableAuto);
1386         }
1387
1388         // PushNamespaceImplicit is called when a prefix/namespace pair is used in an element name, attribute name or some other qualified name.
1389         private void PushNamespaceImplicit(string prefix, string ns) {
1390             NamespaceKind kind;
1391
1392             // See if the prefix is already defined
1393             int existingNsIndex = LookupNamespaceIndex(prefix);
1394
1395             // Prefix is already defined
1396             if (existingNsIndex != -1) {
1397                 // It is defined in the current scope
1398                 if (existingNsIndex > elemScopeStack[elemTop].prevNSTop) {
1399                     // The new namespace Uri needs to be the same as the one that is already declared
1400                     if (nsStack[existingNsIndex].namespaceUri != ns) {
1401                         throw new XmlException(Res.Xml_RedefinePrefix, new string[] { prefix, nsStack[existingNsIndex].namespaceUri, ns });
1402                     }
1403                     // No additional work needed
1404                     return;
1405                 }
1406                 // The prefix is defined but in a different scope
1407                 else {
1408                     // existing declaration is special one (xml, xmlns) -> validate that the new one is the same and can be declared 
1409                     if (nsStack[existingNsIndex].kind == NamespaceKind.Special) {
1410                         if (prefix == "xml") {
1411                             if (ns != nsStack[existingNsIndex].namespaceUri) {
1412                                 throw new ArgumentException(Res.GetString(Res.Xml_XmlPrefix));
1413                             }
1414                             else {
1415                                 kind = NamespaceKind.Implied;
1416                             }
1417                         }
1418                         else {
1419                             Debug.Assert(prefix == "xmlns");
1420                             throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
1421                         }
1422                     }
1423                     // regular namespace declaration -> compare the namespace Uris to decide if the prefix is redefined
1424                     else {
1425                         kind = (nsStack[existingNsIndex].namespaceUri == ns) ? NamespaceKind.Implied : NamespaceKind.NeedToWrite;
1426                     }
1427                 }
1428             }
1429             // No existing declaration found in the namespace stack
1430             else {
1431                 // validate special declaration (xml, xmlns)
1432                 if ((ns == XmlReservedNs.NsXml && prefix != "xml") ||
1433                      (ns == XmlReservedNs.NsXmlNs && prefix != "xmlns")) {
1434                     throw new ArgumentException(Res.GetString(Res.Xml_NamespaceDeclXmlXmlns, prefix));
1435                 }
1436
1437                 // check if it can be found in the predefinedNamespaces (which are provided by the user)
1438                 if (predefinedNamespaces != null) {
1439                     string definedNs = predefinedNamespaces.LookupNamespace(prefix);
1440                     // compare the namespace Uri to decide if the prefix is redefined
1441                     kind = (definedNs == ns) ? NamespaceKind.Implied : NamespaceKind.NeedToWrite;
1442                 }
1443                 else {
1444                     // Namespace not declared anywhere yet, we need to write it out
1445                     kind = NamespaceKind.NeedToWrite;
1446                 }
1447             }
1448
1449             AddNamespace(prefix, ns, kind);
1450         }
1451
1452         // PushNamespaceExplicit is called when a namespace declaration is written out;
1453         // It returs true if the namespace declaration should we written out, false if it should be omited (if OmitDuplicateNamespaceDeclarations is true)
1454         private bool PushNamespaceExplicit(string prefix, string ns) {
1455             bool writeItOut = true;
1456
1457             // See if the prefix is already defined
1458             int existingNsIndex = LookupNamespaceIndex(prefix);
1459
1460             // Existing declaration in the current scope
1461             if (existingNsIndex != -1) {
1462                 // It is defined in the current scope
1463                 if (existingNsIndex > elemScopeStack[elemTop].prevNSTop) {
1464                     // The new namespace Uri needs to be the same as the one that is already declared
1465                     if (nsStack[existingNsIndex].namespaceUri != ns) {
1466                         throw new XmlException(Res.Xml_RedefinePrefix, new string[] { prefix, nsStack[existingNsIndex].namespaceUri, ns });
1467                     }
1468                     // Check for duplicate declarations
1469                     NamespaceKind existingNsKind = nsStack[existingNsIndex].kind;
1470                     if (existingNsKind == NamespaceKind.Written) {
1471                         throw DupAttrException((prefix.Length == 0) ? string.Empty : "xmlns", (prefix.Length == 0) ? "xmlns" : prefix);
1472                     }
1473                     // Check if it can be omitted
1474                     if (omitDuplNamespaces && existingNsKind != NamespaceKind.NeedToWrite) {
1475                         writeItOut = false;
1476                     }
1477                     nsStack[existingNsIndex].kind = NamespaceKind.Written;
1478                     // No additional work needed
1479                     return writeItOut;
1480                 }
1481                 // The prefix is defined but in a different scope
1482                 else {
1483                     // check if is the same and can be omitted
1484                     if (nsStack[existingNsIndex].namespaceUri == ns && omitDuplNamespaces) {
1485                         writeItOut = false;
1486                     }
1487                 }
1488             }
1489             // No existing declaration found in the namespace stack
1490             else {
1491                 // check if it can be found in the predefinedNamespaces (which are provided by the user)
1492                 if (predefinedNamespaces != null) {
1493                     string definedNs = predefinedNamespaces.LookupNamespace(prefix);
1494                     // compare the namespace Uri to decide if the prefix is redefined
1495                     if (definedNs == ns && omitDuplNamespaces) {
1496                         writeItOut = false;
1497                     }
1498                 }
1499             }
1500
1501             // validate special declaration (xml, xmlns)
1502             if ((ns == XmlReservedNs.NsXml && prefix != "xml") ||
1503                  (ns == XmlReservedNs.NsXmlNs && prefix != "xmlns")) {
1504                 throw new ArgumentException(Res.GetString(Res.Xml_NamespaceDeclXmlXmlns, prefix));
1505             }
1506             if (prefix.Length > 0 && prefix[0] == 'x') {
1507                 if (prefix == "xml") {
1508                     if (ns != XmlReservedNs.NsXml) {
1509                         throw new ArgumentException(Res.GetString(Res.Xml_XmlPrefix));
1510                     }
1511                 }
1512                 else if (prefix == "xmlns") {
1513                     throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
1514                 }
1515             }
1516
1517             AddNamespace(prefix, ns, NamespaceKind.Written);
1518
1519             return writeItOut;
1520         }
1521
1522         private void AddNamespace(string prefix, string ns, NamespaceKind kind) {
1523             int top = ++nsTop;
1524             if (top == nsStack.Length) {
1525                 Namespace[] newStack = new Namespace[top * 2];
1526                 Array.Copy(nsStack, newStack, top);
1527                 nsStack = newStack;
1528             }
1529             nsStack[top].Set(prefix, ns, kind);
1530
1531             if (useNsHashtable) {
1532                 // add last
1533                 AddToNamespaceHashtable(nsTop);
1534             }
1535             else if (nsTop == MaxNamespacesWalkCount) {
1536                 // add all
1537                 nsHashtable = new Dictionary<string, int>(hasher);
1538                 for (int i = 0; i <= nsTop; i++) {
1539                     AddToNamespaceHashtable(i);
1540                 }
1541                 useNsHashtable = true;
1542             }
1543         }
1544
1545         private void AddToNamespaceHashtable(int namespaceIndex) {
1546             string prefix = nsStack[namespaceIndex].prefix;
1547             int existingNsIndex;
1548             if (nsHashtable.TryGetValue(prefix, out existingNsIndex)) {
1549                 nsStack[namespaceIndex].prevNsIndex = existingNsIndex;
1550             }
1551             nsHashtable[prefix] = namespaceIndex;
1552         }
1553
1554         private int LookupNamespaceIndex(string prefix) {
1555             int index;
1556             if (useNsHashtable) {
1557                 if (nsHashtable.TryGetValue(prefix, out index)) {
1558                     return index;
1559                 }
1560             }
1561             else {
1562                 for (int i = nsTop; i >= 0; i--) {
1563                     if (nsStack[i].prefix == prefix) {
1564                         return i;
1565                     }
1566                 }
1567             }
1568             return -1;
1569         }
1570
1571         private void PopNamespaces(int indexFrom, int indexTo) {
1572             Debug.Assert(useNsHashtable);
1573             Debug.Assert(indexFrom <= indexTo);
1574             for (int i = indexTo; i >= indexFrom; i--) {
1575                 Debug.Assert(nsHashtable.ContainsKey(nsStack[i].prefix));
1576                 if (nsStack[i].prevNsIndex == -1) {
1577                     nsHashtable.Remove(nsStack[i].prefix);
1578                 }
1579                 else {
1580                     nsHashtable[nsStack[i].prefix] = nsStack[i].prevNsIndex;
1581                 }
1582             }
1583         }
1584
1585         static private XmlException DupAttrException(string prefix, string localName) {
1586             StringBuilder sb = new StringBuilder();
1587             if (prefix.Length > 0) {
1588                 sb.Append(prefix);
1589                 sb.Append(':');
1590             }
1591             sb.Append(localName);
1592             return new XmlException(Res.Xml_DupAttributeName, sb.ToString());
1593         }
1594
1595         // Advance the state machine
1596         private void AdvanceState(Token token) {
1597             if ((int)currentState >= (int)State.Closed) {
1598                 if (currentState == State.Closed || currentState == State.Error) {
1599                     throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
1600                 }
1601                 else {
1602                     throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], GetStateName(currentState)));
1603                 }
1604             }
1605
1606         Advance:
1607             State newState = stateTable[((int)token << 4) + (int)currentState];
1608             //                         [ (int)token * 16 + (int)currentState ];
1609
1610             if ((int)newState >= (int)State.Error) {
1611                 switch (newState) {
1612                     case State.Error:
1613                         ThrowInvalidStateTransition(token, currentState);
1614                         break;
1615
1616                     case State.StartContent:
1617                         StartElementContent();
1618                         newState = State.Content;
1619                         break;
1620
1621                     case State.StartContentEle:
1622                         StartElementContent();
1623                         newState = State.Element;
1624                         break;
1625
1626                     case State.StartContentB64:
1627                         StartElementContent();
1628                         newState = State.B64Content;
1629                         break;
1630
1631                     case State.StartDoc:
1632                         WriteStartDocument();
1633                         newState = State.Document;
1634                         break;
1635
1636                     case State.StartDocEle:
1637                         WriteStartDocument();
1638                         newState = State.Element;
1639                         break;
1640
1641                     case State.EndAttrSEle:
1642                         WriteEndAttribute();
1643                         StartElementContent();
1644                         newState = State.Element;
1645                         break;
1646
1647                     case State.EndAttrEEle:
1648                         WriteEndAttribute();
1649                         StartElementContent();
1650                         newState = State.Content;
1651                         break;
1652
1653                     case State.EndAttrSCont:
1654                         WriteEndAttribute();
1655                         StartElementContent();
1656                         newState = State.Content;
1657                         break;
1658
1659                     case State.EndAttrSAttr:
1660                         WriteEndAttribute();
1661                         newState = State.Attribute;
1662                         break;
1663
1664                     case State.PostB64Cont:
1665                         if (rawWriter != null) {
1666                             rawWriter.WriteEndBase64();
1667                         }
1668                         currentState = State.Content;
1669                         goto Advance;
1670
1671                     case State.PostB64Attr:
1672                         if (rawWriter != null) {
1673                             rawWriter.WriteEndBase64();
1674                         }
1675                         currentState = State.Attribute;
1676                         goto Advance;
1677
1678                     case State.PostB64RootAttr:
1679                         if (rawWriter != null) {
1680                             rawWriter.WriteEndBase64();
1681                         }
1682                         currentState = State.RootLevelAttr;
1683                         goto Advance;
1684
1685                     case State.StartFragEle:
1686                         StartFragment();
1687                         newState = State.Element;
1688                         break;
1689
1690                     case State.StartFragCont:
1691                         StartFragment();
1692                         newState = State.Content;
1693                         break;
1694
1695                     case State.StartFragB64:
1696                         StartFragment();
1697                         newState = State.B64Content;
1698                         break;
1699
1700                     case State.StartRootLevelAttr:
1701                         WriteEndAttribute();
1702                         newState = State.RootLevelAttr;
1703                         break;
1704
1705                     default:
1706                         Debug.Assert(false, "We should not get to this point.");
1707                         break;
1708                 }
1709             }
1710
1711             currentState = newState;
1712         }
1713
1714         private void StartElementContent() {
1715             // write namespace declarations
1716             int start = elemScopeStack[elemTop].prevNSTop;
1717             for (int i = nsTop; i > start; i--) {
1718                 if (nsStack[i].kind == NamespaceKind.NeedToWrite) {
1719                     nsStack[i].WriteDecl(writer, rawWriter);
1720                 }
1721             }
1722
1723             if (rawWriter != null) {
1724                 rawWriter.StartElementContent();
1725             }
1726         }
1727
1728         private static string GetStateName(State state) {
1729             if (state >= State.Error) {
1730                 Debug.Assert(false, "We should never get to this point. State = " + state);
1731                 return "Error";
1732             }
1733             else {
1734                 return stateName[(int)state];
1735             }
1736         }
1737
1738         internal string LookupNamespace(string prefix) {
1739             for (int i = nsTop; i >= 0; i--) {
1740                 if (nsStack[i].prefix == prefix) {
1741                     return nsStack[i].namespaceUri;
1742                 }
1743             }
1744             return (predefinedNamespaces != null) ? predefinedNamespaces.LookupNamespace(prefix) : null;
1745         }
1746
1747         private string LookupLocalNamespace(string prefix) {
1748             for (int i = nsTop; i > elemScopeStack[elemTop].prevNSTop; i--) {
1749                 if (nsStack[i].prefix == prefix) {
1750                     return nsStack[i].namespaceUri;
1751                 }
1752             }
1753             return null;
1754         }
1755
1756         private string GeneratePrefix() {
1757             string genPrefix = "p" + (nsTop - 2).ToString("d", CultureInfo.InvariantCulture);
1758             if (LookupNamespace(genPrefix) == null) {
1759                 return genPrefix;
1760             }
1761             int i = 0;
1762
1763             string s;
1764             do {
1765                 s = string.Concat(genPrefix, i.ToString(CultureInfo.InvariantCulture));
1766                 i++;
1767             } while (LookupNamespace(s) != null);
1768             return s;
1769         }
1770
1771 #if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE
1772         [System.Security.SecuritySafeCritical]
1773 #endif
1774         private unsafe void CheckNCName(string ncname) {
1775             Debug.Assert(ncname != null && ncname.Length > 0);
1776
1777             int i;
1778             int endPos = ncname.Length;
1779
1780             // Check if first character is StartNCName (inc. surrogates)
1781             if ((xmlCharType.charProperties[ncname[0]] & XmlCharType.fNCStartNameSC) != 0) { // if ( xmlCharType.IsStartNCNameChar( ncname[0] ) ) {
1782                 i = 1;
1783             }
1784 #if XML10_FIFTH_EDITION
1785             else if (xmlCharType.IsNCNameSurrogateChar(ncname, 0)) { // surrogate ranges are same for NCName and StartNCName
1786                 i = 2;
1787             }
1788 #endif
1789             else {
1790                 throw InvalidCharsException(ncname, 0);
1791             }
1792
1793             // Check if following characters are NCName (inc. surrogates)
1794             while (i < endPos) {
1795                 if ((xmlCharType.charProperties[ncname[i]] & XmlCharType.fNCNameSC) != 0) { // if ( xmlCharType.IsNCNameChar( ncname[i] ) ) {
1796                     i++;
1797                 }
1798 #if XML10_FIFTH_EDITION
1799                 else if (xmlCharType.IsNCNameSurrogateChar(ncname, i)) {
1800                     i += 2;
1801                 }
1802 #endif
1803                 else {
1804                     throw InvalidCharsException(ncname, i);
1805                 }
1806             }
1807         }
1808
1809         private static Exception InvalidCharsException(string name, int badCharIndex) {
1810             string[] badCharArgs = XmlException.BuildCharExceptionArgs(name, badCharIndex);
1811             string[] args = new string[3];
1812             args[0] = name;
1813             args[1] = badCharArgs[0];
1814             args[2] = badCharArgs[1];
1815             return new ArgumentException(Res.GetString(Res.Xml_InvalidNameCharsDetail, args));
1816         }
1817
1818         // This method translates speficic state transition errors in more friendly error messages
1819         private void ThrowInvalidStateTransition(Token token, State currentState) {
1820             string wrongTokenMessage = Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], GetStateName(currentState));
1821             switch (currentState) {
1822                 case State.AfterRootEle:
1823                 case State.Start:
1824                     if (conformanceLevel == ConformanceLevel.Document) {
1825                         throw new InvalidOperationException(wrongTokenMessage + ' ' + Res.GetString(Res.Xml_ConformanceLevelFragment));
1826                     }
1827                     break;
1828             }
1829             throw new InvalidOperationException(wrongTokenMessage);
1830         }
1831
1832         private bool IsClosedOrErrorState {
1833             get {
1834                 return (int)currentState >= (int)State.Closed;
1835             }
1836         }
1837
1838         private void AddAttribute(string prefix, string localName, string namespaceName) {
1839             int top = attrCount++;
1840             if (top == attrStack.Length) {
1841                 AttrName[] newStack = new AttrName[top * 2];
1842                 Array.Copy(attrStack, newStack, top);
1843                 attrStack = newStack;
1844             }
1845             attrStack[top].Set(prefix, localName, namespaceName);
1846
1847             if (attrCount < MaxAttrDuplWalkCount) {
1848                 // check for duplicates
1849                 for (int i = 0; i < top; i++) {
1850                     if (attrStack[i].IsDuplicate(prefix, localName, namespaceName)) {
1851                         throw DupAttrException(prefix, localName);
1852                     }
1853                 }
1854             }
1855             else {
1856                 // reached the threshold -> add all attributes to hash table
1857                 if (attrCount == MaxAttrDuplWalkCount) {
1858                     if (attrHashTable == null) {
1859                         attrHashTable = new Dictionary<string, int>(hasher);
1860                     }
1861                     Debug.Assert(attrHashTable.Count == 0);
1862                     for (int i = 0; i < top; i++) {
1863                         AddToAttrHashTable(i);
1864                     }
1865                 }
1866
1867                 // add last attribute to hash table and check for duplicates
1868                 AddToAttrHashTable(top);
1869                 int prev = attrStack[top].prev;
1870                 while (prev > 0) {
1871                     // indexes are stored incremented by 1, 0 means no entry
1872                     prev--;
1873                     if (attrStack[prev].IsDuplicate(prefix, localName, namespaceName)) {
1874                         throw DupAttrException(prefix, localName);
1875                     }
1876                     prev = attrStack[prev].prev;
1877                 }
1878             }
1879         }
1880
1881         private void AddToAttrHashTable(int attributeIndex) {
1882             string localName = attrStack[attributeIndex].localName;
1883             int count = attrHashTable.Count;
1884             attrHashTable[localName] = 0; // overwrite on collision
1885             if (count != attrHashTable.Count) {
1886                 return;
1887             }
1888             // chain to previous attribute in stack with the same localName
1889             int prev = attributeIndex - 1;
1890             while (prev >= 0) {
1891                 if (attrStack[prev].localName == localName) {
1892                     break;
1893                 }
1894                 prev--;
1895             }
1896             Debug.Assert(prev >= 0 && attrStack[prev].localName == localName);
1897             attrStack[attributeIndex].prev = prev + 1; // indexes are stored incremented by 1 
1898         }
1899
1900     }
1901 }