Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / XmlWellFormedWriter.cs
blobe03913edc2a01c7b5fbd1afa74cb0fe5fcab5937
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 //------------------------------------------------------------------------------
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;
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?
20 namespace System.Xml {
22 internal partial class XmlWellFormedWriter : XmlWriter {
24 // Private types used by the XmlWellFormedWriter are defined in XmlWellFormedWriterHelpers.cs
28 // Fields
30 // underlying writer
31 XmlWriter writer;
32 XmlRawWriter rawWriter; // writer as XmlRawWriter
33 IXmlNamespaceResolver predefinedNamespaces; // writer as IXmlNamespaceResolver
35 // namespace management
36 Namespace[] nsStack;
37 int nsTop;
38 Dictionary<string, int> nsHashtable;
39 bool useNsHashtable;
41 // element scoping
42 ElementScope[] elemScopeStack;
43 int elemTop;
45 // attribute tracking
46 AttrName[] attrStack;
47 int attrCount;
48 Dictionary<string, int> attrHashTable;
50 // special attribute caching (xmlns, xml:space, xml:lang)
51 SpecialAttribute specAttr = SpecialAttribute.No;
52 AttributeValueCache attrValueCache;
53 string curDeclPrefix;
55 // state machine
56 State[] stateTable;
57 State currentState;
59 // settings
60 bool checkCharacters;
61 bool omitDuplNamespaces;
62 bool writeEndDocumentOnClose;
64 // actual conformance level
65 ConformanceLevel conformanceLevel;
67 // flags
68 bool dtdWritten;
69 bool xmlDeclFollows;
71 // char type tables
72 XmlCharType xmlCharType = XmlCharType.Instance;
74 // hash randomizer
75 SecureStringHasher hasher;
79 // Constants
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
93 // State tables
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,
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,
132 enum Token {
133 StartDocument,
134 EndDocument,
136 Comment,
137 Dtd,
138 StartElement,
139 EndElement,
140 StartAttribute,
141 EndAttribute,
142 Text,
143 CData,
144 AtomicValue,
145 Base64,
146 RawData,
147 Whitespace,
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
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
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
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
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 */
247 // Constructor & finalizer
249 internal XmlWellFormedWriter(XmlWriter writer, XmlWriterSettings settings) {
250 Debug.Assert(writer != null);
251 Debug.Assert(settings != null);
252 Debug.Assert(MaxNamespacesWalkCount <= 3);
254 this.writer = writer;
256 rawWriter = writer as XmlRawWriter;
257 predefinedNamespaces = writer as IXmlNamespaceResolver;
258 if (rawWriter != null) {
259 rawWriter.NamespaceResolver = new NamespaceResolverProxy(this);
262 checkCharacters = settings.CheckCharacters;
263 omitDuplNamespaces = (settings.NamespaceHandling & NamespaceHandling.OmitDuplicates) != 0;
264 writeEndDocumentOnClose = settings.WriteEndDocumentOnClose;
266 conformanceLevel = settings.ConformanceLevel;
267 stateTable = (conformanceLevel == ConformanceLevel.Document) ? StateTableDocument : StateTableAuto;
269 currentState = State.Start;
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);
277 else {
278 string defaultNs = predefinedNamespaces.LookupNamespace(string.Empty);
279 nsStack[2].Set(string.Empty, (defaultNs == null ? string.Empty : defaultNs), NamespaceKind.Implied);
281 nsTop = 2;
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;
289 attrStack = new AttrName[AttributeArrayInitialSize];
291 hasher = new SecureStringHasher();
295 // XmlWriter implementation
297 public override WriteState WriteState {
298 get {
299 if ((int)currentState <= (int)State.Error) {
300 return state2WriteState[(int)currentState];
302 else {
303 Debug.Assert(false, "Expected currentState <= State.Error ");
304 return WriteState.Error;
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;
317 settings.WriteEndDocumentOnClose = writeEndDocumentOnClose;
318 settings.ReadOnly = true;
319 return settings;
323 public override void WriteStartDocument() {
324 WriteStartDocumentImpl(XmlStandalone.Omit);
327 public override void WriteStartDocument(bool standalone) {
328 WriteStartDocumentImpl(standalone ? XmlStandalone.Yes : XmlStandalone.No);
331 public override void WriteEndDocument() {
332 try {
333 // auto-close all elements
334 while (elemTop > 0) {
335 WriteEndElement();
337 State prevState = currentState;
338 AdvanceState(Token.EndDocument);
340 if (prevState != State.AfterRootEle) {
341 throw new ArgumentException(Res.GetString(Res.Xml_NoRoot));
343 if (rawWriter == null) {
344 writer.WriteEndDocument();
347 catch {
348 currentState = State.Error;
349 throw;
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));
358 XmlConvert.VerifyQName(name, ExceptionType.XmlException);
360 if (conformanceLevel == ConformanceLevel.Fragment) {
361 throw new InvalidOperationException(Res.GetString(Res.Xml_DtdNotAllowedInFragment));
364 AdvanceState(Token.Dtd);
365 if (dtdWritten) {
366 currentState = State.Error;
367 throw new InvalidOperationException(Res.GetString(Res.Xml_DtdAlreadyWritten));
370 if (conformanceLevel == ConformanceLevel.Auto) {
371 conformanceLevel = ConformanceLevel.Document;
372 stateTable = StateTableDocument;
375 int i;
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");
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");
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");
396 // write doctype
397 writer.WriteDocType(name, pubid, sysid, subset);
398 dtdWritten = true;
400 catch {
401 currentState = State.Error;
402 throw;
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));
412 CheckNCName(localName);
414 AdvanceState(Token.StartElement);
416 // lookup prefix / namespace
417 if (prefix == null) {
418 if (ns != null) {
419 prefix = LookupPrefix(ns);
421 if (prefix == null) {
422 prefix = string.Empty;
425 else if (prefix.Length > 0) {
426 CheckNCName(prefix);
427 if (ns == null) {
428 ns = LookupNamespace(prefix);
430 if (ns == null || (ns != null && ns.Length == 0)) {
431 throw new ArgumentException(Res.GetString(Res.Xml_PrefixForEmptyNs));
434 if (ns == null) {
435 ns = LookupNamespace(prefix);
436 if (ns == null) {
437 Debug.Assert(prefix.Length == 0);
438 ns = string.Empty;
442 if (elemTop == 0 && rawWriter != null) {
443 // notify the underlying raw writer about the root level element
444 rawWriter.OnRootElement(conformanceLevel);
447 // write start tag
448 writer.WriteStartElement(prefix, localName, ns);
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;
457 elemScopeStack[top].Set(prefix, localName, ns, nsTop);
459 PushNamespaceImplicit(prefix, ns);
461 if (attrCount >= MaxAttrDuplWalkCount) {
462 attrHashTable.Clear();
464 attrCount = 0;
467 catch {
468 currentState = State.Error;
469 throw;
474 public override void WriteEndElement() {
475 try {
476 AdvanceState(Token.EndElement);
478 int top = elemTop;
479 if (top == 0) {
480 throw new XmlException(Res.Xml_NoStartTag, string.Empty);
483 // write end tag
484 if (rawWriter != null) {
485 elemScopeStack[top].WriteEndElement(rawWriter);
487 else {
488 writer.WriteEndElement();
491 // pop namespaces
492 int prevNsTop = elemScopeStack[top].prevNSTop;
493 if (useNsHashtable && prevNsTop < nsTop) {
494 PopNamespaces(prevNsTop + 1, nsTop);
496 nsTop = prevNsTop;
497 elemTop = --top;
499 // check "one root element" condition for ConformanceLevel.Document
500 if (top == 0) {
501 if (conformanceLevel == ConformanceLevel.Document) {
502 currentState = State.AfterRootEle;
504 else {
505 currentState = State.TopLevel;
509 catch {
510 currentState = State.Error;
511 throw;
515 public override void WriteFullEndElement() {
516 try {
517 AdvanceState(Token.EndElement);
519 int top = elemTop;
520 if (top == 0) {
521 throw new XmlException(Res.Xml_NoStartTag, string.Empty);
524 // write end tag
525 if (rawWriter != null) {
526 elemScopeStack[top].WriteFullEndElement(rawWriter);
528 else {
529 writer.WriteFullEndElement();
532 // pop namespaces
533 int prevNsTop = elemScopeStack[top].prevNSTop;
534 if (useNsHashtable && prevNsTop < nsTop) {
535 PopNamespaces(prevNsTop + 1, nsTop);
537 nsTop = prevNsTop;
538 elemTop = --top;
540 // check "one root element" condition for ConformanceLevel.Document
541 if (top == 0) {
542 if (conformanceLevel == ConformanceLevel.Document) {
543 currentState = State.AfterRootEle;
545 else {
546 currentState = State.TopLevel;
550 catch {
551 currentState = State.Error;
552 throw;
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;
564 else {
565 throw new ArgumentException(Res.GetString(Res.Xml_EmptyLocalName));
568 CheckNCName(localName);
570 AdvanceState(Token.StartAttribute);
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);
579 if (prefix == null) {
580 prefix = string.Empty;
583 if (namespaceName == null) {
584 if (prefix != null && prefix.Length > 0) {
585 namespaceName = LookupNamespace(prefix);
587 if (namespaceName == null) {
588 namespaceName = string.Empty;
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));
597 curDeclPrefix = String.Empty;
598 SetSpecialAttribute(SpecialAttribute.DefaultXmlns);
599 goto SkipPushAndWrite;
601 else if (namespaceName.Length > 0) {
602 prefix = LookupPrefix(namespaceName);
603 if (prefix == null || prefix.Length == 0) {
604 prefix = GeneratePrefix();
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));
614 curDeclPrefix = localName;
615 SetSpecialAttribute(SpecialAttribute.PrefixedXmlns);
616 goto SkipPushAndWrite;
618 else if (prefix == "xml") {
619 if (namespaceName.Length > 0 && namespaceName != XmlReservedNs.NsXml) {
620 throw new ArgumentException(Res.GetString(Res.Xml_XmlPrefix));
622 switch (localName) {
623 case "space":
624 SetSpecialAttribute(SpecialAttribute.XmlSpace);
625 goto SkipPushAndWrite;
626 case "lang":
627 SetSpecialAttribute(SpecialAttribute.XmlLang);
628 goto SkipPushAndWrite;
633 CheckNCName(prefix);
635 if (namespaceName.Length == 0) {
636 // attributes cannot have default namespace
637 prefix = string.Empty;
639 else {
640 string definedNs = LookupLocalNamespace(prefix);
641 if (definedNs != null && definedNs != namespaceName) {
642 prefix = GeneratePrefix();
647 if (prefix.Length != 0) {
648 PushNamespaceImplicit(prefix, namespaceName);
651 SkipPushAndWrite:
653 // add attribute to the list and check for duplicates
654 AddAttribute( prefix, localName, namespaceName );
656 if (specAttr == SpecialAttribute.No) {
657 // write attribute name
658 writer.WriteStartAttribute( prefix, localName, namespaceName );
661 catch {
662 currentState = State.Error;
663 throw;
667 public override void WriteEndAttribute() {
668 try {
669 AdvanceState(Token.EndAttribute);
671 if (specAttr != SpecialAttribute.No) {
672 string value;
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();
684 else {
685 rawWriter.WriteNamespaceDeclaration(string.Empty, value);
688 else {
689 writer.WriteStartAttribute(string.Empty, "xmlns", XmlReservedNs.NsXmlNs);
690 attrValueCache.Replay(writer);
691 writer.WriteEndAttribute();
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));
701 if (value == XmlReservedNs.NsXmlNs || (value == XmlReservedNs.NsXml && curDeclPrefix != "xml")) {
702 throw new ArgumentException(Res.GetString(Res.Xml_CanNotBindToReservedNamespace));
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();
711 else {
712 rawWriter.WriteNamespaceDeclaration(curDeclPrefix, value);
715 else {
716 writer.WriteStartAttribute("xmlns", curDeclPrefix, XmlReservedNs.NsXmlNs);
717 attrValueCache.Replay(writer);
718 writer.WriteEndAttribute();
721 curDeclPrefix = null;
722 break;
723 case SpecialAttribute.XmlSpace:
724 attrValueCache.Trim();
725 value = attrValueCache.StringValue;
727 if (value == "default") {
728 elemScopeStack[elemTop].xmlSpace = XmlSpace.Default;
730 else if (value == "preserve") {
731 elemScopeStack[elemTop].xmlSpace = XmlSpace.Preserve;
733 else {
734 throw new ArgumentException(Res.GetString(Res.Xml_InvalidXmlSpace, value));
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;
748 specAttr = SpecialAttribute.No;
749 attrValueCache.Clear();
751 else {
752 writer.WriteEndAttribute();
755 catch {
756 currentState = State.Error;
757 throw;
761 public override void WriteCData(string text) {
762 try {
763 if (text == null) {
764 text = string.Empty;
766 AdvanceState(Token.CData);
767 writer.WriteCData(text);
769 catch {
770 currentState = State.Error;
771 throw;
775 public override void WriteComment(string text) {
776 try {
777 if (text == null) {
778 text = string.Empty;
780 AdvanceState(Token.Comment);
781 writer.WriteComment(text);
783 catch {
784 currentState = State.Error;
785 throw;
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));
795 CheckNCName(name);
797 // check text
798 if (text == null) {
799 text = string.Empty;
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));
808 xmlDeclFollows = true;
809 AdvanceState(Token.PI);
811 if (rawWriter != null) {
812 // Translate PI into an xml declaration
813 rawWriter.WriteXmlDeclaration(text);
815 else {
816 writer.WriteProcessingInstruction(name, text);
819 else {
820 AdvanceState(Token.PI);
821 writer.WriteProcessingInstruction(name, text);
824 catch {
825 currentState = State.Error;
826 throw;
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));
836 CheckNCName(name);
838 AdvanceState(Token.Text);
839 if (SaveAttrValue) {
840 attrValueCache.WriteEntityRef(name);
842 else {
843 writer.WriteEntityRef(name);
846 catch {
847 currentState = State.Error;
848 throw;
852 public override void WriteCharEntity(char ch) {
853 try {
854 if (Char.IsSurrogate(ch)) {
855 throw new ArgumentException(Res.GetString(Res.Xml_InvalidSurrogateMissingLowChar));
858 AdvanceState(Token.Text);
859 if (SaveAttrValue) {
860 attrValueCache.WriteCharEntity(ch);
862 else {
863 writer.WriteCharEntity(ch);
866 catch {
867 currentState = State.Error;
868 throw;
872 public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
873 try {
874 if (!Char.IsSurrogatePair(highChar, lowChar)) {
875 throw XmlConvert.CreateInvalidSurrogatePairException(lowChar, highChar);
878 AdvanceState(Token.Text);
879 if (SaveAttrValue) {
880 attrValueCache.WriteSurrogateCharEntity(lowChar, highChar);
882 else {
883 writer.WriteSurrogateCharEntity(lowChar, highChar);
886 catch {
887 currentState = State.Error;
888 throw;
892 public override void WriteWhitespace(string ws) {
893 try {
894 if (ws == null) {
895 ws = string.Empty;
897 if (!XmlCharType.Instance.IsOnlyWhitespace(ws)) {
898 throw new ArgumentException(Res.GetString(Res.Xml_NonWhitespace));
901 AdvanceState(Token.Whitespace);
902 if (SaveAttrValue) {
903 attrValueCache.WriteWhitespace(ws);
905 else {
906 writer.WriteWhitespace(ws);
909 catch {
910 currentState = State.Error;
911 throw;
915 public override void WriteString(string text) {
916 try {
917 if (text == null) {
918 return;
921 AdvanceState(Token.Text);
922 if (SaveAttrValue) {
923 attrValueCache.WriteString(text);
925 else {
926 writer.WriteString(text);
929 catch {
930 currentState = State.Error;
931 throw;
935 public override void WriteChars(char[] buffer, int index, int count) {
936 try {
937 if (buffer == null) {
938 throw new ArgumentNullException("buffer");
940 if (index < 0) {
941 throw new ArgumentOutOfRangeException("index");
943 if (count < 0) {
944 throw new ArgumentOutOfRangeException("count");
946 if (count > buffer.Length - index) {
947 throw new ArgumentOutOfRangeException("count");
950 AdvanceState(Token.Text);
951 if (SaveAttrValue) {
952 attrValueCache.WriteChars(buffer, index, count);
954 else {
955 writer.WriteChars(buffer, index, count);
958 catch {
959 currentState = State.Error;
960 throw;
964 public override void WriteRaw(char[] buffer, int index, int count) {
965 try {
966 if (buffer == null) {
967 throw new ArgumentNullException("buffer");
969 if (index < 0) {
970 throw new ArgumentOutOfRangeException("index");
972 if (count < 0) {
973 throw new ArgumentOutOfRangeException("count");
975 if (count > buffer.Length - index) {
976 throw new ArgumentOutOfRangeException("count");
979 AdvanceState(Token.RawData);
980 if (SaveAttrValue) {
981 attrValueCache.WriteRaw(buffer, index, count);
983 else {
984 writer.WriteRaw(buffer, index, count);
987 catch {
988 currentState = State.Error;
989 throw;
993 public override void WriteRaw(string data) {
994 try {
995 if (data == null) {
996 return;
999 AdvanceState(Token.RawData);
1000 if (SaveAttrValue) {
1001 attrValueCache.WriteRaw(data);
1003 else {
1004 writer.WriteRaw(data);
1007 catch {
1008 currentState = State.Error;
1009 throw;
1013 public override void WriteBase64(byte[] buffer, int index, int count) {
1014 try {
1015 if (buffer == null) {
1016 throw new ArgumentNullException("buffer");
1018 if (index < 0) {
1019 throw new ArgumentOutOfRangeException("index");
1021 if (count < 0) {
1022 throw new ArgumentOutOfRangeException("count");
1024 if (count > buffer.Length - index) {
1025 throw new ArgumentOutOfRangeException("count");
1028 AdvanceState(Token.Base64);
1029 writer.WriteBase64(buffer, index, count);
1031 catch {
1032 currentState = State.Error;
1033 throw;
1037 public override void Close() {
1038 if (currentState != State.Closed) {
1039 try {
1040 if (writeEndDocumentOnClose) {
1041 while (currentState != State.Error && elemTop > 0) {
1042 WriteEndElement();
1045 else {
1046 if (currentState != State.Error && elemTop > 0) {
1047 //finish the start element tag '>'
1048 try {
1049 AdvanceState(Token.EndElement);
1051 catch {
1052 currentState = State.Error;
1053 throw;
1058 if (InBase64 && rawWriter != null) {
1059 rawWriter.WriteEndBase64();
1062 writer.Flush();
1064 finally {
1065 try {
1066 if (rawWriter != null) {
1067 rawWriter.Close(WriteState);
1069 else {
1070 writer.Close();
1073 finally {
1074 currentState = State.Closed;
1080 public override void Flush() {
1081 try {
1082 writer.Flush();
1084 catch {
1085 currentState = State.Error;
1086 throw;
1090 public override string LookupPrefix(string ns) {
1091 try {
1092 if (ns == null) {
1093 throw new ArgumentNullException("ns");
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;
1103 return prefix;
1106 return (predefinedNamespaces != null) ? predefinedNamespaces.LookupPrefix(ns) : null;
1108 catch {
1109 currentState = State.Error;
1110 throw;
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;
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;
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));
1137 CheckNCName(localName);
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));
1147 prefix = GeneratePrefix();
1148 PushNamespaceImplicit(prefix, ns);
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(":");
1158 WriteString(localName);
1160 else {
1161 rawWriter.WriteQualifiedName(prefix, localName, ns);
1164 catch {
1165 currentState = State.Error;
1166 throw;
1170 public override void WriteValue(bool value) {
1171 try {
1172 AdvanceState(Token.AtomicValue);
1173 writer.WriteValue(value);
1175 catch {
1176 currentState = State.Error;
1177 throw;
1181 public override void WriteValue(DateTime value) {
1182 try {
1183 AdvanceState(Token.AtomicValue);
1184 writer.WriteValue(value);
1186 catch {
1187 currentState = State.Error;
1188 throw;
1192 public override void WriteValue(DateTimeOffset value) {
1193 try {
1194 AdvanceState(Token.AtomicValue);
1195 writer.WriteValue(value);
1197 catch {
1198 currentState = State.Error;
1199 throw;
1203 public override void WriteValue(double value) {
1204 try {
1205 AdvanceState(Token.AtomicValue);
1206 writer.WriteValue(value);
1208 catch {
1209 currentState = State.Error;
1210 throw;
1214 public override void WriteValue(float value) {
1215 try {
1216 AdvanceState(Token.AtomicValue);
1217 writer.WriteValue(value);
1219 catch {
1220 currentState = State.Error;
1221 throw;
1225 public override void WriteValue(decimal value) {
1226 try {
1227 AdvanceState(Token.AtomicValue);
1228 writer.WriteValue(value);
1230 catch {
1231 currentState = State.Error;
1232 throw;
1236 public override void WriteValue(int value) {
1237 try {
1238 AdvanceState(Token.AtomicValue);
1239 writer.WriteValue(value);
1241 catch {
1242 currentState = State.Error;
1243 throw;
1247 public override void WriteValue(long value) {
1248 try {
1249 AdvanceState(Token.AtomicValue);
1250 writer.WriteValue(value);
1252 catch {
1253 currentState = State.Error;
1254 throw;
1258 public override void WriteValue(string value) {
1259 try {
1260 if (value == null) {
1261 return;
1263 if (SaveAttrValue) {
1264 AdvanceState(Token.Text);
1265 attrValueCache.WriteValue(value);
1267 else {
1268 AdvanceState(Token.AtomicValue);
1269 writer.WriteValue(value);
1272 catch {
1273 currentState = State.Error;
1274 throw;
1278 public override void WriteValue(object value) {
1279 try {
1280 if (SaveAttrValue && value is string) {
1281 AdvanceState(Token.Text);
1282 attrValueCache.WriteValue((string)value);
1284 else {
1285 AdvanceState(Token.AtomicValue);
1286 writer.WriteValue(value);
1289 catch {
1290 currentState = State.Error;
1291 throw;
1295 public override void WriteBinHex(byte[] buffer, int index, int count) {
1296 if (IsClosedOrErrorState) {
1297 throw new InvalidOperationException(Res.GetString(Res.Xml_ClosedOrError));
1299 try {
1300 AdvanceState(Token.Text);
1301 base.WriteBinHex(buffer, index, count);
1303 catch {
1304 currentState = State.Error;
1305 throw;
1310 // Internal methods
1312 #if !SILVERLIGHT
1313 internal XmlWriter InnerWriter {
1314 get {
1315 return this.writer;
1319 internal XmlRawWriter RawWriter {
1320 get {
1321 return rawWriter;
1324 #endif
1327 // Private methods
1329 private bool SaveAttrValue {
1330 get {
1331 return specAttr != SpecialAttribute.No;
1335 private bool InBase64 {
1336 get {
1337 return (currentState == State.B64Content || currentState == State.B64Attribute || currentState == State.RootLevelB64Attr);
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");
1350 if (attrValueCache == null) {
1351 attrValueCache = new AttributeValueCache();
1355 private void WriteStartDocumentImpl(XmlStandalone standalone) {
1356 try {
1357 AdvanceState(Token.StartDocument);
1359 if (conformanceLevel == ConformanceLevel.Auto) {
1360 conformanceLevel = ConformanceLevel.Document;
1361 stateTable = StateTableDocument;
1363 else if (conformanceLevel == ConformanceLevel.Fragment) {
1364 throw new InvalidOperationException(Res.GetString(Res.Xml_CannotStartDocumentOnFragment));
1367 if (rawWriter != null) {
1368 if (!xmlDeclFollows) {
1369 rawWriter.WriteXmlDeclaration(standalone);
1372 else {
1373 // We do not pass the standalone value here - Dev10 Bug #479769
1374 writer.WriteStartDocument();
1377 catch {
1378 currentState = State.Error;
1379 throw;
1383 private void StartFragment() {
1384 conformanceLevel = ConformanceLevel.Fragment;
1385 Debug.Assert(stateTable == StateTableAuto);
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;
1392 // See if the prefix is already defined
1393 int existingNsIndex = LookupNamespaceIndex(prefix);
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 });
1403 // No additional work needed
1404 return;
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));
1414 else {
1415 kind = NamespaceKind.Implied;
1418 else {
1419 Debug.Assert(prefix == "xmlns");
1420 throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
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;
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));
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;
1443 else {
1444 // Namespace not declared anywhere yet, we need to write it out
1445 kind = NamespaceKind.NeedToWrite;
1449 AddNamespace(prefix, ns, kind);
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;
1457 // See if the prefix is already defined
1458 int existingNsIndex = LookupNamespaceIndex(prefix);
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 });
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);
1473 // Check if it can be omitted
1474 if (omitDuplNamespaces && existingNsKind != NamespaceKind.NeedToWrite) {
1475 writeItOut = false;
1477 nsStack[existingNsIndex].kind = NamespaceKind.Written;
1478 // No additional work needed
1479 return writeItOut;
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;
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;
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));
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));
1512 else if (prefix == "xmlns") {
1513 throw new ArgumentException(Res.GetString(Res.Xml_XmlnsPrefix));
1517 AddNamespace(prefix, ns, NamespaceKind.Written);
1519 return writeItOut;
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;
1529 nsStack[top].Set(prefix, ns, kind);
1531 if (useNsHashtable) {
1532 // add last
1533 AddToNamespaceHashtable(nsTop);
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);
1541 useNsHashtable = true;
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;
1551 nsHashtable[prefix] = namespaceIndex;
1554 private int LookupNamespaceIndex(string prefix) {
1555 int index;
1556 if (useNsHashtable) {
1557 if (nsHashtable.TryGetValue(prefix, out index)) {
1558 return index;
1561 else {
1562 for (int i = nsTop; i >= 0; i--) {
1563 if (nsStack[i].prefix == prefix) {
1564 return i;
1568 return -1;
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);
1579 else {
1580 nsHashtable[nsStack[i].prefix] = nsStack[i].prevNsIndex;
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(':');
1591 sb.Append(localName);
1592 return new XmlException(Res.Xml_DupAttributeName, sb.ToString());
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));
1601 else {
1602 throw new InvalidOperationException(Res.GetString(Res.Xml_WrongToken, tokenName[(int)token], GetStateName(currentState)));
1606 Advance:
1607 State newState = stateTable[((int)token << 4) + (int)currentState];
1608 // [ (int)token * 16 + (int)currentState ];
1610 if ((int)newState >= (int)State.Error) {
1611 switch (newState) {
1612 case State.Error:
1613 ThrowInvalidStateTransition(token, currentState);
1614 break;
1616 case State.StartContent:
1617 StartElementContent();
1618 newState = State.Content;
1619 break;
1621 case State.StartContentEle:
1622 StartElementContent();
1623 newState = State.Element;
1624 break;
1626 case State.StartContentB64:
1627 StartElementContent();
1628 newState = State.B64Content;
1629 break;
1631 case State.StartDoc:
1632 WriteStartDocument();
1633 newState = State.Document;
1634 break;
1636 case State.StartDocEle:
1637 WriteStartDocument();
1638 newState = State.Element;
1639 break;
1641 case State.EndAttrSEle:
1642 WriteEndAttribute();
1643 StartElementContent();
1644 newState = State.Element;
1645 break;
1647 case State.EndAttrEEle:
1648 WriteEndAttribute();
1649 StartElementContent();
1650 newState = State.Content;
1651 break;
1653 case State.EndAttrSCont:
1654 WriteEndAttribute();
1655 StartElementContent();
1656 newState = State.Content;
1657 break;
1659 case State.EndAttrSAttr:
1660 WriteEndAttribute();
1661 newState = State.Attribute;
1662 break;
1664 case State.PostB64Cont:
1665 if (rawWriter != null) {
1666 rawWriter.WriteEndBase64();
1668 currentState = State.Content;
1669 goto Advance;
1671 case State.PostB64Attr:
1672 if (rawWriter != null) {
1673 rawWriter.WriteEndBase64();
1675 currentState = State.Attribute;
1676 goto Advance;
1678 case State.PostB64RootAttr:
1679 if (rawWriter != null) {
1680 rawWriter.WriteEndBase64();
1682 currentState = State.RootLevelAttr;
1683 goto Advance;
1685 case State.StartFragEle:
1686 StartFragment();
1687 newState = State.Element;
1688 break;
1690 case State.StartFragCont:
1691 StartFragment();
1692 newState = State.Content;
1693 break;
1695 case State.StartFragB64:
1696 StartFragment();
1697 newState = State.B64Content;
1698 break;
1700 case State.StartRootLevelAttr:
1701 WriteEndAttribute();
1702 newState = State.RootLevelAttr;
1703 break;
1705 default:
1706 Debug.Assert(false, "We should not get to this point.");
1707 break;
1711 currentState = newState;
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);
1723 if (rawWriter != null) {
1724 rawWriter.StartElementContent();
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";
1733 else {
1734 return stateName[(int)state];
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;
1744 return (predefinedNamespaces != null) ? predefinedNamespaces.LookupNamespace(prefix) : null;
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;
1753 return null;
1756 private string GeneratePrefix() {
1757 string genPrefix = "p" + (nsTop - 2).ToString("d", CultureInfo.InvariantCulture);
1758 if (LookupNamespace(genPrefix) == null) {
1759 return genPrefix;
1761 int i = 0;
1763 string s;
1764 do {
1765 s = string.Concat(genPrefix, i.ToString(CultureInfo.InvariantCulture));
1766 i++;
1767 } while (LookupNamespace(s) != null);
1768 return s;
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);
1777 int i;
1778 int endPos = ncname.Length;
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;
1784 #if XML10_FIFTH_EDITION
1785 else if (xmlCharType.IsNCNameSurrogateChar(ncname, 0)) { // surrogate ranges are same for NCName and StartNCName
1786 i = 2;
1788 #endif
1789 else {
1790 throw InvalidCharsException(ncname, 0);
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++;
1798 #if XML10_FIFTH_EDITION
1799 else if (xmlCharType.IsNCNameSurrogateChar(ncname, i)) {
1800 i += 2;
1802 #endif
1803 else {
1804 throw InvalidCharsException(ncname, i);
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));
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));
1827 break;
1829 throw new InvalidOperationException(wrongTokenMessage);
1832 private bool IsClosedOrErrorState {
1833 get {
1834 return (int)currentState >= (int)State.Closed;
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;
1845 attrStack[top].Set(prefix, localName, namespaceName);
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);
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);
1861 Debug.Assert(attrHashTable.Count == 0);
1862 for (int i = 0; i < top; i++) {
1863 AddToAttrHashTable(i);
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);
1876 prev = attrStack[prev].prev;
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;
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;
1894 prev--;
1896 Debug.Assert(prev >= 0 && attrStack[prev].localName == localName);
1897 attrStack[attributeIndex].prev = prev + 1; // indexes are stored incremented by 1