The Labs \ Source Viewer \ SSCLI \ System.Xml \ XmlWriter

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlWriter.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.IO;
  17. using System.Text;
  18. using System.Xml.XPath;
  19. using System.Xml.Schema;
  20. using System.Diagnostics;
  21. using System.Collections;
  22. using System.Globalization;
  23. namespace System.Xml
  24. {
  25.    
  26.     // Specifies the state of the XmlWriter.
  27.     public enum WriteState
  28.     {
  29.         // Nothing has been written yet.
  30.         Start,
  31.        
  32.         // Writing the prolog.
  33.         Prolog,
  34.        
  35.         // Writing a the start tag for an element.
  36.         Element,
  37.        
  38.         // Writing an attribute value.
  39.         Attribute,
  40.        
  41.         // Writing element content.
  42.         Content,
  43.        
  44.         // XmlWriter is closed; Close has been called.
  45.         Closed,
  46.        
  47.         // Writer is in error state.
  48.         Error
  49.     }
  50.    
  51.     // Represents a writer that provides fast non-cached forward-only way of generating XML streams containing XML documents
  52.     // that conform to the W3C Extensible Markup Language (XML) 1.0 specification and the Namespaces in XML specification.
  53.     public abstract class XmlWriter : IDisposable
  54.     {
  55.         // Helper buffer for WriteNode(XmlReader, bool)
  56.         char[] writeNodeBuffer;
  57.        
  58.         // Constants
  59.         const int WriteNodeBufferSize = 1024;
  60.        
  61.         // Settings
  62.         // Returns the settings describing the features of the the writer. Returns null for V1 XmlWriters (XmlTextWriter).
  63.         public virtual XmlWriterSettings Settings {
  64.             get { return null; }
  65.         }
  66.        
  67.         // Write methods
  68.         // Writes out the XML declaration with the version "1.0".
  69.         public abstract void WriteStartDocument();
  70.        
  71.         //Writes out the XML declaration with the version "1.0" and the speficied standalone attribute.
  72.         public abstract void WriteStartDocument(bool standalone);
  73.        
  74.         //Closes any open elements or attributes and puts the writer back in the Start state.
  75.         public abstract void WriteEndDocument();
  76.        
  77.         // Writes out the DOCTYPE declaration with the specified name and optional attributes.
  78.         public abstract void WriteDocType(string name, string pubid, string sysid, string subset);
  79.        
  80.         // Writes out the specified start tag and associates it with the given namespace.
  81.         public void WriteStartElement(string localName, string ns)
  82.         {
  83.             WriteStartElement(null, localName, ns);
  84.         }
  85.        
  86.         // Writes out the specified start tag and associates it with the given namespace and prefix.
  87.         public abstract void WriteStartElement(string prefix, string localName, string ns);
  88.        
  89.         // Writes out a start tag with the specified local name with no namespace.
  90.         public void WriteStartElement(string localName)
  91.         {
  92.             WriteStartElement(null, localName, (string)null);
  93.         }
  94.        
  95.         // Closes one element and pops the corresponding namespace scope.
  96.         public abstract void WriteEndElement();
  97.        
  98.         // Closes one element and pops the corresponding namespace scope. Writes out a full end element tag, e.g. </element>.
  99.         public abstract void WriteFullEndElement();
  100.        
  101.         // Writes out the attribute with the specified LocalName, value, and NamespaceURI.
  102.         public void WriteAttributeString(string localName, string ns, string value)
  103.         {
  104.             WriteStartAttribute(null, localName, ns);
  105.             WriteString(value);
  106.             WriteEndAttribute();
  107.         }
  108.        
  109.         // Writes out the attribute with the specified LocalName and value.
  110.         public void WriteAttributeString(string localName, string value)
  111.         {
  112.             WriteStartAttribute(null, localName, (string)null);
  113.             WriteString(value);
  114.             WriteEndAttribute();
  115.         }
  116.        
  117.         // Writes out the attribute with the specified prefix, LocalName, NamespaceURI and value.
  118.         public void WriteAttributeString(string prefix, string localName, string ns, string value)
  119.         {
  120.             WriteStartAttribute(prefix, localName, ns);
  121.             WriteString(value);
  122.             WriteEndAttribute();
  123.         }
  124.        
  125.         // Writes the start of an attribute.
  126.         public void WriteStartAttribute(string localName, string ns)
  127.         {
  128.             WriteStartAttribute(null, localName, ns);
  129.         }
  130.        
  131.         // Writes the start of an attribute.
  132.         public abstract void WriteStartAttribute(string prefix, string localName, string ns);
  133.        
  134.         // Writes the start of an attribute.
  135.         public void WriteStartAttribute(string localName)
  136.         {
  137.             WriteStartAttribute(null, localName, (string)null);
  138.         }
  139.        
  140.         // Closes the attribute opened by WriteStartAttribute call.
  141.         public abstract void WriteEndAttribute();
  142.        
  143.         // Writes out a <![CDATA[...]]>; block containing the specified text.
  144.         public abstract void WriteCData(string text);
  145.        
  146.         // Writes out a comment <!--...-->; containing the specified text.
  147.         public abstract void WriteComment(string text);
  148.        
  149.         // Writes out a processing instruction with a space between the name and text as follows: <?name text?>
  150.         public abstract void WriteProcessingInstruction(string name, string text);
  151.        
  152.         // Writes out an entity reference as follows: "&"+name+";".
  153.         public abstract void WriteEntityRef(string name);
  154.        
  155.         // Forces the generation of a character entity for the specified Unicode character value.
  156.         public abstract void WriteCharEntity(char ch);
  157.        
  158.         // Writes out the given whitespace.
  159.         public abstract void WriteWhitespace(string ws);
  160.        
  161.         // Writes out the specified text content.
  162.         public abstract void WriteString(string text);
  163.        
  164.         // Write out the given surrogate pair as an entity reference.
  165.         public abstract void WriteSurrogateCharEntity(char lowChar, char highChar);
  166.        
  167.         // Writes out the specified text content.
  168.         public abstract void WriteChars(char[] buffer, int index, int count);
  169.        
  170.         // Writes raw markup from the given character buffer.
  171.         public abstract void WriteRaw(char[] buffer, int index, int count);
  172.        
  173.         // Writes raw markup from the given string.
  174.         public abstract void WriteRaw(string data);
  175.        
  176.         // Encodes the specified binary bytes as base64 and writes out the resulting text.
  177.         public abstract void WriteBase64(byte[] buffer, int index, int count);
  178.        
  179.         // Encodes the specified binary bytes as binhex and writes out the resulting text.
  180.         public virtual void WriteBinHex(byte[] buffer, int index, int count)
  181.         {
  182.             BinHexEncoder.Encode(buffer, index, count, this);
  183.         }
  184.        
  185.         // Returns the state of the XmlWriter.
  186.         public abstract WriteState WriteState {
  187.             get;
  188.         }
  189.        
  190.         // Closes the XmlWriter and the underlying stream/TextReader (if Settings.CloseOutput is true).
  191.         public abstract void Close();
  192.        
  193.         // Flushes data that is in the internal buffers into the underlying streams/TextReader and flushes the stream/TextReader.
  194.         public abstract void Flush();
  195.        
  196.         // Returns the closest prefix defined in the current namespace scope for the specified namespace URI.
  197.         public abstract string LookupPrefix(string ns);
  198.        
  199.         // Gets an XmlSpace representing the current xml:space scope.
  200.         public virtual XmlSpace XmlSpace {
  201.             get { return XmlSpace.Default; }
  202.         }
  203.        
  204.         // Gets the current xml:lang scope.
  205.         public virtual string XmlLang {
  206.             get { return string.Empty; }
  207.         }
  208.        
  209.         // Scalar Value Methods
  210.        
  211.         // Writes out the specified name, ensuring it is a valid NmToken according to the XML specification
  212.         // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name).
  213.         public virtual void WriteNmToken(string name)
  214.         {
  215.             if (name == null || name.Length == 0) {
  216.                 throw new ArgumentException(Res.GetString(Res.Xml_EmptyName));
  217.             }
  218.             WriteString(XmlConvert.VerifyNMTOKEN(name, ExceptionType.ArgumentException));
  219.         }
  220.        
  221.         // Writes out the specified name, ensuring it is a valid Name according to the XML specification
  222.         // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name).
  223.         public virtual void WriteName(string name)
  224.         {
  225.             WriteString(XmlConvert.VerifyQName(name, ExceptionType.ArgumentException));
  226.         }
  227.        
  228.         // Writes out the specified namespace-qualified name by looking up the prefix that is in scope for the given namespace.
  229.         public virtual void WriteQualifiedName(string localName, string ns)
  230.         {
  231.             if (ns != null && ns.Length > 0) {
  232.                 string prefix = LookupPrefix(ns);
  233.                 if (prefix == null) {
  234.                     throw new ArgumentException(Res.GetString(Res.Xml_UndefNamespace, ns));
  235.                 }
  236.                 WriteString(prefix);
  237.                 WriteString(":");
  238.             }
  239.             WriteString(localName);
  240.         }
  241.        
  242.         // Writes out the specified value.
  243.         public virtual void WriteValue(object value)
  244.         {
  245.             if (value == null) {
  246.                 throw new ArgumentNullException("value");
  247.             }
  248.             WriteString(XmlUntypedConverter.Untyped.ToString(value, null));
  249.         }
  250.        
  251.         // Writes out the specified value.
  252.         public virtual void WriteValue(string value)
  253.         {
  254.             if (value == null) {
  255.                 throw new ArgumentNullException("value");
  256.             }
  257.             WriteString(value);
  258.         }
  259.        
  260.         // Writes out the specified value.
  261.         public virtual void WriteValue(bool value)
  262.         {
  263.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  264.         }
  265.        
  266.         // Writes out the specified value.
  267.         public virtual void WriteValue(DateTime value)
  268.         {
  269.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  270.         }
  271.        
  272.         // Writes out the specified value.
  273.         public virtual void WriteValue(double value)
  274.         {
  275.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  276.         }
  277.        
  278.         // Writes out the specified value.
  279.         public virtual void WriteValue(float value)
  280.         {
  281.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  282.         }
  283.        
  284.         // Writes out the specified value.
  285.         public virtual void WriteValue(decimal value)
  286.         {
  287.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  288.         }
  289.        
  290.         // Writes out the specified value.
  291.         public virtual void WriteValue(int value)
  292.         {
  293.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  294.         }
  295.         // Writes out the specified value.
  296.         public virtual void WriteValue(long value)
  297.         {
  298.             WriteString(XmlUntypedConverter.Untyped.ToString(value));
  299.         }
  300.        
  301.         // XmlReader Helper Methods
  302.        
  303.         // Writes out all the attributes found at the current position in the specified XmlReader.
  304.         public virtual void WriteAttributes(XmlReader reader, bool defattr)
  305.         {
  306.            
  307.             if (null == reader) {
  308.                 throw new ArgumentNullException("reader");
  309.             }
  310.            
  311.             if (reader.NodeType == XmlNodeType.Element || reader.NodeType == XmlNodeType.XmlDeclaration) {
  312.                 if (reader.MoveToFirstAttribute()) {
  313.                     WriteAttributes(reader, defattr);
  314.                     reader.MoveToElement();
  315.                 }
  316.             }
  317.             else if (reader.NodeType != XmlNodeType.Attribute) {
  318.                 throw new XmlException(Res.Xml_InvalidPosition, string.Empty);
  319.             }
  320.             else {
  321.                 do {
  322.                     // we need to check both XmlReader.IsDefault and XmlReader.SchemaInfo.IsDefault.
  323.                     // If either of these is true and defattr=false, we should not write the attribute out
  324.                     IXmlSchemaInfo schemaInfo;
  325.                     if (defattr || (!reader.IsDefault && ((schemaInfo = reader.SchemaInfo) == null || !schemaInfo.IsDefault))) {
  326.                         WriteStartAttribute(reader.Prefix, reader.LocalName, reader.NamespaceURI);
  327.                         while (reader.ReadAttributeValue()) {
  328.                             if (reader.NodeType == XmlNodeType.EntityReference) {
  329.                                 WriteEntityRef(reader.Name);
  330.                             }
  331.                             else {
  332.                                 WriteString(reader.Value);
  333.                             }
  334.                         }
  335.                         WriteEndAttribute();
  336.                     }
  337.                 }
  338.                 while (reader.MoveToNextAttribute());
  339.             }
  340.         }
  341.        
  342.         // Copies the current node from the given reader to the writer (including child nodes), and if called on an element moves the XmlReader
  343.         // to the corresponding end element.
  344.         public virtual void WriteNode(XmlReader reader, bool defattr)
  345.         {
  346.             if (null == reader) {
  347.                 throw new ArgumentNullException("reader");
  348.             }
  349.            
  350.             bool canReadChunk = reader.CanReadValueChunk;
  351.             int d = reader.NodeType == XmlNodeType.None ? -1 : reader.Depth;
  352.             do {
  353.                 switch (reader.NodeType) {
  354.                     case XmlNodeType.Element:
  355.                         WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
  356.                         WriteAttributes(reader, defattr);
  357.                         if (reader.IsEmptyElement) {
  358.                             WriteEndElement();
  359.                             break;
  360.                         }
  361.                         break;
  362.                     case XmlNodeType.Text:
  363.                         if (canReadChunk) {
  364.                             if (writeNodeBuffer == null) {
  365.                                 writeNodeBuffer = new char[WriteNodeBufferSize];
  366.                             }
  367.                             int read;
  368.                             while ((read = reader.ReadValueChunk(writeNodeBuffer, 0, WriteNodeBufferSize)) > 0) {
  369.                                 this.WriteChars(writeNodeBuffer, 0, read);
  370.                             }
  371.                         }
  372.                         else {
  373.                             WriteString(reader.Value);
  374.                         }
  375.                         break;
  376.                     case XmlNodeType.Whitespace:
  377.                     case XmlNodeType.SignificantWhitespace:
  378.                         WriteWhitespace(reader.Value);
  379.                         break;
  380.                     case XmlNodeType.CDATA:
  381.                         WriteCData(reader.Value);
  382.                         break;
  383.                     case XmlNodeType.EntityReference:
  384.                         WriteEntityRef(reader.Name);
  385.                         break;
  386.                     case XmlNodeType.XmlDeclaration:
  387.                     case XmlNodeType.ProcessingInstruction:
  388.                         WriteProcessingInstruction(reader.Name, reader.Value);
  389.                         break;
  390.                     case XmlNodeType.DocumentType:
  391.                         WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
  392.                         break;
  393.                     case XmlNodeType.Comment:
  394.                        
  395.                         WriteComment(reader.Value);
  396.                         break;
  397.                     case XmlNodeType.EndElement:
  398.                         WriteFullEndElement();
  399.                         break;
  400.                 }
  401.             }
  402.             while (reader.Read() && (d < reader.Depth || (d == reader.Depth && reader.NodeType == XmlNodeType.EndElement)));
  403.         }
  404.        
  405.         // Copies the current node from the given XPathNavigator to the writer (including child nodes).
  406.         public virtual void WriteNode(XPathNavigator navigator, bool defattr)
  407.         {
  408.             if (navigator == null) {
  409.                 throw new ArgumentNullException("navigator");
  410.             }
  411.             int iLevel = 0;
  412.            
  413.             navigator = navigator.Clone();
  414.            
  415.             while (true) {
  416.                 bool mayHaveChildren = false;
  417.                 XPathNodeType nodeType = navigator.NodeType;
  418.                
  419.                 switch (nodeType) {
  420.                     case XPathNodeType.Element:
  421.                         WriteStartElement(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  422.                        
  423.                         // Copy attributes
  424.                         if (navigator.MoveToFirstAttribute()) {
  425.                             do {
  426.                                 IXmlSchemaInfo schemaInfo = navigator.SchemaInfo;
  427.                                 if (defattr || (schemaInfo == null || !schemaInfo.IsDefault)) {
  428.                                     WriteStartAttribute(navigator.Prefix, navigator.LocalName, navigator.NamespaceURI);
  429.                                     // copy string value to writer
  430.                                     WriteString(navigator.Value);
  431.                                     WriteEndAttribute();
  432.                                 }
  433.                             }
  434.                             while (navigator.MoveToNextAttribute());
  435.                             navigator.MoveToParent();
  436.                         }
  437.                        
  438.                         // Copy namespaces
  439.                         if (navigator.MoveToFirstNamespace(XPathNamespaceScope.Local)) {
  440.                             WriteLocalNamespaces(navigator);
  441.                             navigator.MoveToParent();
  442.                         }
  443.                         mayHaveChildren = true;
  444.                         break;
  445.                     case XPathNodeType.Attribute:
  446.                         // do nothing on root level attribute
  447.                         break;
  448.                     case XPathNodeType.Text:
  449.                         WriteString(navigator.Value);
  450.                         break;
  451.                     case XPathNodeType.SignificantWhitespace:
  452.                     case XPathNodeType.Whitespace:
  453.                         WriteWhitespace(navigator.Value);
  454.                         break;
  455.                     case XPathNodeType.Root:
  456.                         mayHaveChildren = true;
  457.                         break;
  458.                     case XPathNodeType.Comment:
  459.                         WriteComment(navigator.Value);
  460.                         break;
  461.                     case XPathNodeType.ProcessingInstruction:
  462.                         WriteProcessingInstruction(navigator.LocalName, navigator.Value);
  463.                         break;
  464.                     case XPathNodeType.Namespace:
  465.                         // do nothing on root level namespace
  466.                         break;
  467.                     default:
  468.                         Debug.Assert(false);
  469.                         break;
  470.                 }
  471.                
  472.                 if (mayHaveChildren) {
  473.                     // If children exist, move down to next level
  474.                     if (navigator.MoveToFirstChild()) {
  475.                         iLevel++;
  476.                         continue;
  477.                     }
  478.                     else {
  479.                         // EndElement
  480.                         if (navigator.NodeType == XPathNodeType.Element) {
  481.                             if (navigator.IsEmptyElement) {
  482.                                 WriteEndElement();
  483.                             }
  484.                             else {
  485.                                 WriteFullEndElement();
  486.                             }
  487.                         }
  488.                     }
  489.                 }
  490.                
  491.                 // No children
  492.                 while (true) {
  493.                     if (iLevel == 0) {
  494.                         // The entire subtree has been copied
  495.                         return;
  496.                     }
  497.                    
  498.                     if (navigator.MoveToNext()) {
  499.                         // Found a sibling, so break to outer loop
  500.                         break;
  501.                     }
  502.                    
  503.                     // No siblings, so move up to previous level
  504.                     iLevel--;
  505.                     navigator.MoveToParent();
  506.                    
  507.                     // EndElement
  508.                     if (navigator.NodeType == XPathNodeType.Element)
  509.                         WriteFullEndElement();
  510.                 }
  511.             }
  512.         }
  513.        
  514.         // Element Helper Methods
  515.        
  516.         // Writes out an element with the specified name containing the specified string value.
  517.         public void WriteElementString(string localName, string value)
  518.         {
  519.             WriteElementString(localName, null, value);
  520.         }
  521.        
  522.         // Writes out an attribute with the specified name, namespace URI and string value.
  523.         public void WriteElementString(string localName, string ns, string value)
  524.         {
  525.             WriteStartElement(localName, ns);
  526.             if (null != value && 0 != value.Length) {
  527.                 WriteString(value);
  528.             }
  529.             WriteEndElement();
  530.         }
  531.        
  532.         // Writes out an attribute with the specified name, namespace URI, and string value.
  533.         public void WriteElementString(string prefix, string localName, string ns, string value)
  534.         {
  535.             WriteStartElement(prefix, localName, ns);
  536.             if (null != value && 0 != value.Length) {
  537.                 WriteString(value);
  538.             }
  539.             WriteEndElement();
  540.         }
  541.        
  542.         void IDisposable.Dispose()
  543.         {
  544.             Dispose(true);
  545.         }
  546.        
  547.         // Dispose the underline stream objects (calls Close on the XmlWriter)
  548.         protected virtual void Dispose(bool disposing)
  549.         {
  550.             if (WriteState != WriteState.Closed) {
  551.                 Close();
  552.             }
  553.         }
  554.        
  555.         // Copy local namespaces on the navigator's current node to the raw writer. The namespaces are returned by the navigator in reversed order.
  556.         // The recursive call reverses them back.
  557.         private void WriteLocalNamespaces(XPathNavigator nsNav)
  558.         {
  559.             string prefix = nsNav.LocalName;
  560.             string ns = nsNav.Value;
  561.            
  562.             if (nsNav.MoveToNextNamespace(XPathNamespaceScope.Local)) {
  563.                 WriteLocalNamespaces(nsNav);
  564.             }
  565.            
  566.             if (prefix.Length == 0) {
  567.                 WriteAttributeString(string.Empty, "xmlns", XmlReservedNs.NsXmlNs, ns);
  568.             }
  569.             else {
  570.                 WriteAttributeString("xmlns", prefix, XmlReservedNs.NsXmlNs, ns);
  571.             }
  572.         }
  573.        
  574.         //
  575.         // Static methods for creating writers
  576.         //
  577.         // Creates an XmlWriter for writing into the provided file.
  578.         public static XmlWriter Create(string outputFileName)
  579.         {
  580.             return Create(outputFileName, (XmlWriterSettings)null);
  581.         }
  582.        
  583.         // Creates an XmlWriter for writing into the provided file with the specified settings.
  584.         public static XmlWriter Create(string outputFileName, XmlWriterSettings settings)
  585.         {
  586.             if (outputFileName == null) {
  587.                 throw new ArgumentNullException("outputFileName");
  588.             }
  589.             if (settings == null) {
  590.                 settings = new XmlWriterSettings();
  591.             }
  592.             FileStream fs = null;
  593.             try {
  594.                 fs = new FileStream(outputFileName, FileMode.Create, FileAccess.Write, FileShare.Read);
  595.                 return CreateWriterImpl(fs, settings.Encoding, true, settings);
  596.             }
  597.             catch {
  598.                 if (fs != null) {
  599.                     fs.Close();
  600.                 }
  601.                 throw;
  602.             }
  603.         }
  604.        
  605.         // Creates an XmlWriter for writing into the provided stream.
  606.         public static XmlWriter Create(Stream output)
  607.         {
  608.             return Create(output, (XmlWriterSettings)null);
  609.         }
  610.        
  611.         // Creates an XmlWriter for writing into the provided stream with the specified settings.
  612.         public static XmlWriter Create(Stream output, XmlWriterSettings settings)
  613.         {
  614.             if (output == null) {
  615.                 throw new ArgumentNullException("output");
  616.             }
  617.             if (settings == null) {
  618.                 settings = new XmlWriterSettings();
  619.             }
  620.             return CreateWriterImpl(output, settings.Encoding, settings.CloseOutput, settings);
  621.         }
  622.        
  623.         // Creates an XmlWriter for writing into the provided TextWriter.
  624.         public static XmlWriter Create(TextWriter output)
  625.         {
  626.             return Create(output, null);
  627.         }
  628.        
  629.         // Creates an XmlWriter for writing into the provided TextWriter with the specified settings.
  630.         public static XmlWriter Create(TextWriter output, XmlWriterSettings settings)
  631.         {
  632.             if (output == null) {
  633.                 throw new ArgumentNullException("output");
  634.             }
  635.             if (settings == null) {
  636.                 settings = new XmlWriterSettings();
  637.             }
  638.             return CreateWriterImpl(output, settings);
  639.         }
  640.        
  641.         // Creates an XmlWriter for writing into the provided StringBuilder.
  642.         public static XmlWriter Create(StringBuilder output)
  643.         {
  644.             return Create(output, null);
  645.         }
  646.        
  647.         // Creates an XmlWriter for writing into the provided StringBuilder with the specified settings.
  648.         public static XmlWriter Create(StringBuilder output, XmlWriterSettings settings)
  649.         {
  650.             if (output == null) {
  651.                 throw new ArgumentNullException("output");
  652.             }
  653.             if (settings == null) {
  654.                 settings = new XmlWriterSettings();
  655.             }
  656.             return CreateWriterImpl(new StringWriter(output, CultureInfo.InvariantCulture), settings);
  657.         }
  658.        
  659.         // Creates an XmlWriter wrapped around the provided XmlWriter with the default settings.
  660.         public static XmlWriter Create(XmlWriter output)
  661.         {
  662.             return Create(output, null);
  663.         }
  664.        
  665.         // Creates an XmlWriter wrapped around the provided XmlWriter with the specified settings.
  666.         public static XmlWriter Create(XmlWriter output, XmlWriterSettings settings)
  667.         {
  668.             if (output == null) {
  669.                 throw new ArgumentNullException("output");
  670.             }
  671.             if (settings == null) {
  672.                 settings = new XmlWriterSettings();
  673.             }
  674.             return AddConformanceWrapper(output, output.Settings, settings);
  675.         }
  676.        
  677.         private static XmlWriter CreateWriterImpl(Stream output, Encoding encoding, bool closeOutput, XmlWriterSettings settings)
  678.         {
  679.             Debug.Assert(output != null);
  680.             Debug.Assert(encoding != null);
  681.             Debug.Assert(settings != null);
  682.            
  683.             XmlWriter writer;
  684.            
  685.             if (encoding.CodePage == 65001) {
  686.                 // If the encoding is UTF-8, create a special-purpose writer
  687.                 switch (settings.OutputMethod) {
  688.                     case XmlOutputMethod.Xml:
  689.                         if (settings.Indent) {
  690.                             writer = new XmlUtf8RawTextWriterIndent(output, encoding, settings, closeOutput);
  691.                         }
  692.                         else {
  693.                             writer = new XmlUtf8RawTextWriter(output, encoding, settings, closeOutput);
  694.                         }
  695.                         break;
  696.                     case XmlOutputMethod.Html:
  697.                         if (settings.Indent) {
  698.                             writer = new HtmlUtf8RawTextWriterIndent(output, encoding, settings, closeOutput);
  699.                         }
  700.                         else {
  701.                             writer = new HtmlUtf8RawTextWriter(output, encoding, settings, closeOutput);
  702.                         }
  703.                         break;
  704.                     case XmlOutputMethod.Text:
  705.                         writer = new TextUtf8RawTextWriter(output, encoding, settings, closeOutput);
  706.                         break;
  707.                     case XmlOutputMethod.AutoDetect:
  708.                         writer = new XmlAutoDetectWriter(output, encoding, settings);
  709.                         break;
  710.                     default:
  711.                         Debug.Assert(false, "Invalid XmlOutputMethod setting.");
  712.                         return null;
  713.                 }
  714.             }
  715.             else {
  716.                 // Otherwise, create a general-purpose writer than can do any encoding
  717.                 switch (settings.OutputMethod) {
  718.                     case XmlOutputMethod.Xml:
  719.                         if (settings.Indent) {
  720.                             writer = new XmlEncodedRawTextWriterIndent(output, encoding, settings, closeOutput);
  721.                         }
  722.                         else {
  723.                             writer = new XmlEncodedRawTextWriter(output, encoding, settings, closeOutput);
  724.                         }
  725.                         break;
  726.                     case XmlOutputMethod.Html:
  727.                         if (settings.Indent) {
  728.                             writer = new HtmlEncodedRawTextWriterIndent(output, encoding, settings, closeOutput);
  729.                         }
  730.                         else {
  731.                             writer = new HtmlEncodedRawTextWriter(output, encoding, settings, closeOutput);
  732.                         }
  733.                         break;
  734.                     case XmlOutputMethod.Text:
  735.                         writer = new TextEncodedRawTextWriter(output, encoding, settings, closeOutput);
  736.                         break;
  737.                     case XmlOutputMethod.AutoDetect:
  738.                         writer = new XmlAutoDetectWriter(output, encoding, settings);
  739.                         break;
  740.                     default:
  741.                         Debug.Assert(false, "Invalid XmlOutputMethod2 setting.");
  742.                         return null;
  743.                 }
  744.             }
  745.            
  746.             // Wrap with Xslt/XQuery specific writer if needed;
  747.             // XmlOutputMethod.AutoDetect writer does this lazily when it creates the underlying Xml or Html writer.
  748.             if (settings.OutputMethod != XmlOutputMethod.AutoDetect) {
  749.                 if (settings.IsQuerySpecific) {
  750.                     // Create QueryOutputWriter if CData sections or DocType need to be tracked
  751.                     writer = new QueryOutputWriter((XmlRawWriter)writer, settings);
  752.                 }
  753.             }
  754.            
  755.             // wrap with well-formed writer
  756.             writer = new XmlWellFormedWriter(writer, settings);
  757.            
  758.             return writer;
  759.         }
  760.        
  761.         private static XmlWriter CreateWriterImpl(TextWriter output, XmlWriterSettings settings)
  762.         {
  763.             Debug.Assert(output != null);
  764.             Debug.Assert(settings != null);
  765.            
  766.             XmlWriter writer;
  767.            
  768.             switch (settings.OutputMethod) {
  769.                 case XmlOutputMethod.Xml:
  770.                     if (settings.Indent) {
  771.                         writer = new XmlEncodedRawTextWriterIndent(output, settings);
  772.                     }
  773.                     else {
  774.                         writer = new XmlEncodedRawTextWriter(output, settings);
  775.                     }
  776.                     break;
  777.                 case XmlOutputMethod.Html:
  778.                     if (settings.Indent) {
  779.                         writer = new HtmlEncodedRawTextWriterIndent(output, settings);
  780.                     }
  781.                     else {
  782.                         writer = new HtmlEncodedRawTextWriter(output, settings);
  783.                     }
  784.                     break;
  785.                 case XmlOutputMethod.Text:
  786.                     writer = new TextEncodedRawTextWriter(output, settings);
  787.                     break;
  788.                 case XmlOutputMethod.AutoDetect:
  789.                     writer = new XmlAutoDetectWriter(output, settings);
  790.                     break;
  791.                 default:
  792.                     Debug.Assert(false, "Invalid XmlOutputMethod setting.");
  793.                     return null;
  794.             }
  795.            
  796.             // Wrap with Xslt/XQuery specific writer if needed;
  797.             // XmlOutputMethod.AutoDetect writer does this lazily when it creates the underlying Xml or Html writer.
  798.             if (settings.OutputMethod != XmlOutputMethod.AutoDetect) {
  799.                 if (settings.IsQuerySpecific) {
  800.                    
  801.                     // Create QueryOutputWriter if CData sections or DocType need to be tracked
  802.                     writer = new QueryOutputWriter((XmlRawWriter)writer, settings);
  803.                 }
  804.             }
  805.            
  806.             writer = new XmlWellFormedWriter(writer, settings);
  807.             return writer;
  808.         }
  809.        
  810.         private static XmlWriter AddConformanceWrapper(XmlWriter baseWriter, XmlWriterSettings baseWriterSettings, XmlWriterSettings settings)
  811.         {
  812.             ConformanceLevel confLevel = ConformanceLevel.Auto;
  813.             bool checkValues = false;
  814.             bool checkNames = false;
  815.             bool replaceNewLines = false;
  816.             bool needWrap = false;
  817.            
  818.             if (baseWriterSettings == null) {
  819.                 // assume the V1 writer already do all conformance checking;
  820.                 // wrap only if NewLineHandling == Replace or CheckCharacters is true
  821.                 if (settings.NewLineHandling == NewLineHandling.Replace) {
  822.                     replaceNewLines = true;
  823.                     needWrap = true;
  824.                 }
  825.                 if (settings.CheckCharacters) {
  826.                     checkValues = true;
  827.                     needWrap = true;
  828.                 }
  829.             }
  830.             else {
  831.                 if (settings.ConformanceLevel != baseWriterSettings.ConformanceLevel) {
  832.                     confLevel = settings.ConformanceLevel;
  833.                     needWrap = true;
  834.                 }
  835.                 if (settings.CheckCharacters && !baseWriterSettings.CheckCharacters) {
  836.                     checkValues = true;
  837.                     checkNames = confLevel == ConformanceLevel.Auto;
  838.                     needWrap = true;
  839.                 }
  840.                 if (settings.NewLineHandling == NewLineHandling.Replace && baseWriterSettings.NewLineHandling == NewLineHandling.None) {
  841.                     replaceNewLines = true;
  842.                     needWrap = true;
  843.                 }
  844.             }
  845.            
  846.             if (needWrap) {
  847.                 XmlWriter writer = baseWriter;
  848.                 if (confLevel != ConformanceLevel.Auto) {
  849.                     writer = new XmlWellFormedWriter(writer, settings);
  850.                 }
  851.                 if (checkValues || replaceNewLines) {
  852.                     writer = new XmlCharCheckingWriter(writer, checkValues, checkNames, replaceNewLines, settings.NewLineChars);
  853.                 }
  854.                 return writer;
  855.             }
  856.             else {
  857.                 return baseWriter;
  858.             }
  859.         }
  860.     }
  861. }

Developer Fusion