1 //------------------------------------------------------------------------------
2 // <copyright file=QueryOutputWriter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
10 using System.Globalization;
12 using System.Collections.Generic;
13 using System.Xml.Schema;
14 using System.Diagnostics;
18 /// This writer wraps an XmlRawWriter and inserts additional lexical information into the resulting
21 /// 2. DocType declaration
23 /// It also performs well-formed document checks if standalone="yes" and/or a doc-type-decl is output.
25 internal class QueryOutputWriter : XmlRawWriter {
26 private XmlRawWriter wrapped;
27 private bool inCDataSection;
28 private Dictionary<XmlQualifiedName, int> lookupCDataElems;
29 private BitStack bitsCData;
30 private XmlQualifiedName qnameCData;
31 private bool outputDocType, checkWellFormedDoc, hasDocElem, inAttr;
32 private string systemId, publicId;
35 public QueryOutputWriter(XmlRawWriter writer, XmlWriterSettings settings) {
36 this.wrapped = writer;
38 this.systemId = settings.DocTypeSystem;
39 this.publicId = settings.DocTypePublic;
41 if (settings.OutputMethod == XmlOutputMethod.Xml) {
42 // Xml output method shouldn't output doc-type-decl if system ID is not defined (even if public ID is)
43 // Only check for well-formed document if output method is xml
44 if (this.systemId != null) {
45 this.outputDocType = true;
46 this.checkWellFormedDoc = true;
49 // Check for well-formed document if standalone="yes" in an auto-generated xml declaration
50 if (settings.AutoXmlDeclaration && settings.Standalone == XmlStandalone.Yes)
51 this.checkWellFormedDoc = true;
53 if (settings.CDataSectionElements.Count > 0) {
54 this.bitsCData = new BitStack();
55 this.lookupCDataElems = new Dictionary<XmlQualifiedName, int>();
56 this.qnameCData = new XmlQualifiedName();
58 // Add each element name to the lookup table
59 foreach (XmlQualifiedName name in settings.CDataSectionElements) {
60 this.lookupCDataElems[name] = 0;
63 this.bitsCData.PushBit(false);
66 else if (settings.OutputMethod == XmlOutputMethod.Html) {
67 // Html output method should output doc-type-decl if system ID or public ID is defined
68 if (this.systemId != null || this.publicId != null)
69 this.outputDocType = true;
74 //-----------------------------------------------
75 // XmlWriter interface
76 //-----------------------------------------------
79 /// Get and set the namespace resolver that's used by this RawWriter to resolve prefixes.
81 internal override IXmlNamespaceResolver NamespaceResolver {
86 this.resolver = value;
87 this.wrapped.NamespaceResolver = value;
92 /// Write the xml declaration. This must be the first call.
94 internal override void WriteXmlDeclaration(XmlStandalone standalone) {
95 this.wrapped.WriteXmlDeclaration(standalone);
98 internal override void WriteXmlDeclaration(string xmldecl) {
99 this.wrapped.WriteXmlDeclaration(xmldecl);
103 /// Return settings provided to factory.
105 public override XmlWriterSettings Settings {
107 XmlWriterSettings settings = this.wrapped.Settings;
109 settings.ReadOnly = false;
110 settings.DocTypeSystem = this.systemId;
111 settings.DocTypePublic = this.publicId;
112 settings.ReadOnly = true;
119 /// Suppress this explicit call to WriteDocType if information was provided by XmlWriterSettings.
121 public override void WriteDocType(string name, string pubid, string sysid, string subset) {
122 if (this.publicId == null && this.systemId == null) {
123 Debug.Assert(!this.outputDocType);
124 this.wrapped.WriteDocType(name, pubid, sysid, subset);
129 /// Check well-formedness, possibly output doc-type-decl, and determine whether this element is a
130 /// CData section element.
132 public override void WriteStartElement(string prefix, string localName, string ns) {
135 if (this.checkWellFormedDoc) {
136 // Don't allow multiple document elements
137 if (this.depth == 0 && this.hasDocElem)
138 throw new XmlException(Res.Xml_NoMultipleRoots, string.Empty);
141 this.hasDocElem = true;
144 // Output doc-type declaration immediately before first element is output
145 if (this.outputDocType) {
146 this.wrapped.WriteDocType(
147 prefix.Length != 0 ? prefix + ":" + localName : localName,
152 this.outputDocType = false;
155 this.wrapped.WriteStartElement(prefix, localName, ns);
157 if (this.lookupCDataElems != null) {
158 // Determine whether this element is a CData section element
159 this.qnameCData.Init(localName, ns);
160 this.bitsCData.PushBit(this.lookupCDataElems.ContainsKey(this.qnameCData));
164 internal override void WriteEndElement(string prefix, string localName, string ns) {
167 this.wrapped.WriteEndElement(prefix, localName, ns);
169 if (this.checkWellFormedDoc)
172 if (this.lookupCDataElems != null)
173 this.bitsCData.PopBit();
176 internal override void WriteFullEndElement(string prefix, string localName, string ns) {
179 this.wrapped.WriteFullEndElement(prefix, localName, ns);
181 if (this.checkWellFormedDoc)
184 if (this.lookupCDataElems != null)
185 this.bitsCData.PopBit();
188 internal override void StartElementContent() {
189 this.wrapped.StartElementContent();
192 public override void WriteStartAttribute(string prefix, string localName, string ns) {
194 this.wrapped.WriteStartAttribute(prefix, localName, ns);
197 public override void WriteEndAttribute() {
199 this.wrapped.WriteEndAttribute();
202 internal override void WriteNamespaceDeclaration(string prefix, string ns) {
203 this.wrapped.WriteNamespaceDeclaration(prefix, ns);
206 internal override bool SupportsNamespaceDeclarationInChunks {
208 return this.wrapped.SupportsNamespaceDeclarationInChunks;
212 internal override void WriteStartNamespaceDeclaration(string prefix) {
213 this.wrapped.WriteStartNamespaceDeclaration(prefix);
216 internal override void WriteEndNamespaceDeclaration() {
217 this.wrapped.WriteEndNamespaceDeclaration();
220 public override void WriteCData(string text) {
221 this.wrapped.WriteCData(text);
224 public override void WriteComment(string text) {
226 this.wrapped.WriteComment(text);
229 public override void WriteProcessingInstruction(string name, string text) {
231 this.wrapped.WriteProcessingInstruction(name, text);
234 public override void WriteWhitespace(string ws) {
235 if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
236 this.wrapped.WriteCData(ws);
238 this.wrapped.WriteWhitespace(ws);
241 public override void WriteString(string text) {
242 if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
243 this.wrapped.WriteCData(text);
245 this.wrapped.WriteString(text);
248 public override void WriteChars(char[] buffer, int index, int count) {
249 if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
250 this.wrapped.WriteCData(new string(buffer, index, count));
252 this.wrapped.WriteChars(buffer, index, count);
255 public override void WriteEntityRef(string name) {
257 this.wrapped.WriteEntityRef(name);
260 public override void WriteCharEntity(char ch) {
262 this.wrapped.WriteCharEntity(ch);
265 public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
267 this.wrapped.WriteSurrogateCharEntity(lowChar, highChar);
270 public override void WriteRaw(char[] buffer, int index, int count) {
271 if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
272 this.wrapped.WriteCData(new string(buffer, index, count));
274 this.wrapped.WriteRaw(buffer, index, count);
277 public override void WriteRaw(string data) {
278 if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
279 this.wrapped.WriteCData(data);
281 this.wrapped.WriteRaw(data);
284 public override void Close() {
285 this.wrapped.Close();
287 if (this.checkWellFormedDoc && !this.hasDocElem) {
288 // Need at least one document element
289 throw new XmlException(Res.Xml_NoRoot, string.Empty);
293 public override void Flush() {
294 this.wrapped.Flush();
298 //-----------------------------------------------
300 //-----------------------------------------------
303 /// Write CData text if element is a CData element. Return true if text should be written
304 /// within a CData section.
306 private bool StartCDataSection() {
307 Debug.Assert(!this.inCDataSection);
308 if (this.lookupCDataElems != null && this.bitsCData.PeekBit()) {
309 this.inCDataSection = true;
316 /// No longer write CData text.
318 private void EndCDataSection() {
319 this.inCDataSection = false;