Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / QueryOutputWriter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file=QueryOutputWriter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml {
9     using System;
10     using System.Globalization;
11     using System.IO;
12     using System.Collections.Generic;
13     using System.Xml.Schema;
14     using System.Diagnostics;
15
16
17     /// <summary>
18     /// This writer wraps an XmlRawWriter and inserts additional lexical information into the resulting
19     /// Xml 1.0 document:
20     ///   1. CData sections
21     ///   2. DocType declaration
22     ///
23     /// It also performs well-formed document checks if standalone="yes" and/or a doc-type-decl is output.
24     /// </summary>
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;
33         private int depth;
34
35         public QueryOutputWriter(XmlRawWriter writer, XmlWriterSettings settings) {
36             this.wrapped = writer;
37
38             this.systemId = settings.DocTypeSystem;
39             this.publicId = settings.DocTypePublic;
40
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;
47                 }
48
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;
52
53                 if (settings.CDataSectionElements.Count > 0) {
54                     this.bitsCData = new BitStack();
55                     this.lookupCDataElems = new Dictionary<XmlQualifiedName, int>();
56                     this.qnameCData = new XmlQualifiedName();
57
58                     // Add each element name to the lookup table
59                     foreach (XmlQualifiedName name in settings.CDataSectionElements) {
60                         this.lookupCDataElems[name] = 0;
61                     }
62
63                     this.bitsCData.PushBit(false);
64                 }
65             }
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;
70             }
71         }
72
73
74         //-----------------------------------------------
75         // XmlWriter interface
76         //-----------------------------------------------
77
78         /// <summary>
79         /// Get and set the namespace resolver that's used by this RawWriter to resolve prefixes.
80         /// </summary>
81         internal override IXmlNamespaceResolver NamespaceResolver  {
82             get {
83                 return this.resolver;
84             }
85             set {
86                 this.resolver = value;
87                 this.wrapped.NamespaceResolver = value;
88             }
89         }
90
91         /// <summary>
92         /// Write the xml declaration.  This must be the first call.
93         /// </summary>
94         internal override void WriteXmlDeclaration(XmlStandalone standalone) {
95             this.wrapped.WriteXmlDeclaration(standalone);
96         }
97
98         internal override void WriteXmlDeclaration(string xmldecl) {
99             this.wrapped.WriteXmlDeclaration(xmldecl);
100         }
101
102         /// <summary>
103         /// Return settings provided to factory.
104         /// </summary>
105         public override XmlWriterSettings Settings {
106             get {
107                 XmlWriterSettings settings = this.wrapped.Settings;
108
109                 settings.ReadOnly = false;
110                 settings.DocTypeSystem = this.systemId;
111                 settings.DocTypePublic = this.publicId;
112                 settings.ReadOnly = true;
113
114                 return settings;
115             }
116         }
117
118         /// <summary>
119         /// Suppress this explicit call to WriteDocType if information was provided by XmlWriterSettings.
120         /// </summary>
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);
125             }
126         }
127
128         /// <summary>
129         /// Check well-formedness, possibly output doc-type-decl, and determine whether this element is a
130         /// CData section element.
131         /// </summary>
132         public override void WriteStartElement(string prefix, string localName, string ns) {
133             EndCDataSection();
134
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);
139
140                 this.depth++;
141                 this.hasDocElem = true;
142             }
143
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,
148                         this.publicId,
149                         this.systemId,
150                         null);
151
152                 this.outputDocType = false;
153             }
154
155             this.wrapped.WriteStartElement(prefix, localName, ns);
156
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));
161             }
162         }
163
164         internal override void WriteEndElement(string prefix, string localName, string ns) {
165             EndCDataSection();
166
167             this.wrapped.WriteEndElement(prefix, localName, ns);
168
169             if (this.checkWellFormedDoc)
170                 this.depth--;
171
172             if (this.lookupCDataElems != null)
173                 this.bitsCData.PopBit();
174         }
175
176         internal override void WriteFullEndElement(string prefix, string localName, string ns) {
177             EndCDataSection();
178
179             this.wrapped.WriteFullEndElement(prefix, localName, ns);
180
181             if (this.checkWellFormedDoc)
182                 this.depth--;
183
184             if (this.lookupCDataElems != null)
185                 this.bitsCData.PopBit();
186         }
187
188         internal override void StartElementContent() {
189             this.wrapped.StartElementContent();
190         }
191
192         public override void WriteStartAttribute(string prefix, string localName, string ns) {
193             this.inAttr = true;
194             this.wrapped.WriteStartAttribute(prefix, localName, ns);
195         }
196
197         public override void WriteEndAttribute() {
198             this.inAttr = false;
199             this.wrapped.WriteEndAttribute();
200         }
201
202         internal override void WriteNamespaceDeclaration(string prefix, string ns) {
203             this.wrapped.WriteNamespaceDeclaration(prefix, ns);
204         }
205
206         internal override bool SupportsNamespaceDeclarationInChunks {
207             get {
208                 return this.wrapped.SupportsNamespaceDeclarationInChunks;
209             }
210         }
211
212         internal override void WriteStartNamespaceDeclaration(string prefix) {
213             this.wrapped.WriteStartNamespaceDeclaration(prefix);
214         }
215
216         internal override void WriteEndNamespaceDeclaration() {
217             this.wrapped.WriteEndNamespaceDeclaration();
218         }
219
220         public override void WriteCData(string text) {
221             this.wrapped.WriteCData(text);
222         }
223
224         public override void WriteComment(string text) {
225             EndCDataSection();
226             this.wrapped.WriteComment(text);
227         }
228
229         public override void WriteProcessingInstruction(string name, string text) {
230             EndCDataSection();
231             this.wrapped.WriteProcessingInstruction(name, text);
232         }
233
234         public override void WriteWhitespace(string ws) {
235             if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
236                 this.wrapped.WriteCData(ws);
237             else
238                 this.wrapped.WriteWhitespace(ws);
239         }
240
241         public override void WriteString(string text) {
242             if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
243                 this.wrapped.WriteCData(text);
244             else
245                 this.wrapped.WriteString(text);
246         }
247
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));
251             else
252                 this.wrapped.WriteChars(buffer, index, count);
253         }
254
255         public override void WriteEntityRef(string name) {
256             EndCDataSection();
257             this.wrapped.WriteEntityRef(name);
258         }
259
260         public override void WriteCharEntity(char ch) {
261             EndCDataSection();
262             this.wrapped.WriteCharEntity(ch);
263         }
264
265         public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
266             EndCDataSection();
267             this.wrapped.WriteSurrogateCharEntity(lowChar, highChar);
268         }
269
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));
273             else
274                 this.wrapped.WriteRaw(buffer, index, count);
275         }
276
277         public override void WriteRaw(string data) {
278             if (!this.inAttr && (this.inCDataSection || StartCDataSection()))
279                 this.wrapped.WriteCData(data);
280             else
281                 this.wrapped.WriteRaw(data);
282         }
283
284         public override void Close() {
285             this.wrapped.Close();
286
287             if (this.checkWellFormedDoc && !this.hasDocElem) {
288                 // Need at least one document element
289                 throw new XmlException(Res.Xml_NoRoot, string.Empty);
290             }
291         }
292
293         public override void Flush() {
294             this.wrapped.Flush();
295         }
296
297
298         //-----------------------------------------------
299         // Helper methods
300         //-----------------------------------------------
301
302         /// <summary>
303         /// Write CData text if element is a CData element.  Return true if text should be written
304         /// within a CData section.
305         /// </summary>
306         private bool StartCDataSection() {
307             Debug.Assert(!this.inCDataSection);
308             if (this.lookupCDataElems != null && this.bitsCData.PeekBit()) {
309                 this.inCDataSection = true;
310                 return true;
311             }
312             return false;
313         }
314
315         /// <summary>
316         /// No longer write CData text.
317         /// </summary>
318         private void EndCDataSection() {
319             this.inCDataSection = false;
320         }
321     }
322 }
323