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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlAutoDetectWriter.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. namespace System.Xml
  16. {
  17.     using System;
  18.     using System.Diagnostics;
  19.     using System.IO;
  20.     using System.Text;
  21.     using System.Xml;
  22.     using System.Xml.Schema;
  23.    
  24.    
  25.     /// <summary>
  26.     /// This writer implements XmlOutputMethod.AutoDetect. If the first element is "html", then output will be
  27.     /// directed to an Html writer. Otherwise, output will be directed to an Xml writer.
  28.     /// </summary>
  29.     internal class XmlAutoDetectWriter : XmlRawWriter, IRemovableWriter
  30.     {
  31.         private XmlRawWriter wrapped;
  32.         private OnRemoveWriter onRemove;
  33.         private XmlWriterSettings writerSettings;
  34.         private XmlEventCache eventCache;
  35.         // Cache up events until first StartElement is encountered
  36.         private TextWriter textWriter;
  37.         private Stream strm;
  38.        
  39.         //-----------------------------------------------
  40.         // Constructors
  41.         //-----------------------------------------------
  42.        
  43.         private XmlAutoDetectWriter(XmlWriterSettings writerSettings, Encoding encoding)
  44.         {
  45.             Debug.Assert(writerSettings.OutputMethod == XmlOutputMethod.AutoDetect);
  46.            
  47.             this.writerSettings = writerSettings.Clone();
  48.             this.writerSettings.Encoding = encoding;
  49.             this.writerSettings.ReadOnly = true;
  50.            
  51.             // Start caching all events
  52.             this.eventCache = new XmlEventCache(string.Empty, true);
  53.         }
  54.        
  55.         public XmlAutoDetectWriter(TextWriter textWriter, XmlWriterSettings writerSettings) : this(writerSettings, textWriter.Encoding)
  56.         {
  57.             this.textWriter = textWriter;
  58.         }
  59.        
  60.         public XmlAutoDetectWriter(Stream strm, Encoding encoding, XmlWriterSettings writerSettings) : this(writerSettings, encoding)
  61.         {
  62.             this.strm = strm;
  63.         }
  64.        
  65.        
  66.         //-----------------------------------------------
  67.         // IRemovableWriter interface
  68.         //-----------------------------------------------
  69.        
  70.         /// <summary>
  71.         /// This writer will raise this event once it has determined whether to replace itself with the Html or Xml writer.
  72.         /// </summary>
  73.         public OnRemoveWriter OnRemoveWriterEvent {
  74.             get { return this.onRemove; }
  75.             set { this.onRemove = value; }
  76.         }
  77.        
  78.        
  79.         //-----------------------------------------------
  80.         // XmlWriter interface
  81.         //-----------------------------------------------
  82.        
  83.         public override XmlWriterSettings Settings {
  84.             get { return this.writerSettings; }
  85.         }
  86.        
  87.         public override void WriteDocType(string name, string pubid, string sysid, string subset)
  88.         {
  89.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  90.             this.wrapped.WriteDocType(name, pubid, sysid, subset);
  91.         }
  92.        
  93.         public override void WriteStartElement(string prefix, string localName, string ns)
  94.         {
  95.             if (this.wrapped == null) {
  96.                 // This is the first time WriteStartElement has been called, so create the Xml or Html writer
  97.                 if (ns.Length == 0 && IsHtmlTag(localName))
  98.                     CreateWrappedWriter(XmlOutputMethod.Html);
  99.                 else
  100.                     CreateWrappedWriter(XmlOutputMethod.Xml);
  101.             }
  102.             this.wrapped.WriteStartElement(prefix, localName, ns);
  103.         }
  104.        
  105.         public override void WriteStartAttribute(string prefix, string localName, string ns)
  106.         {
  107.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  108.             this.wrapped.WriteStartAttribute(prefix, localName, ns);
  109.         }
  110.        
  111.         public override void WriteEndAttribute()
  112.         {
  113.             Debug.Assert(this.wrapped != null);
  114.             this.wrapped.WriteEndAttribute();
  115.         }
  116.        
  117.         public override void WriteCData(string text)
  118.         {
  119.             if (TextBlockCreatesWriter(text))
  120.                 this.wrapped.WriteCData(text);
  121.             else
  122.                 this.eventCache.WriteCData(text);
  123.         }
  124.        
  125.         public override void WriteComment(string text)
  126.         {
  127.             if (this.wrapped == null)
  128.                 this.eventCache.WriteComment(text);
  129.             else
  130.                 this.wrapped.WriteComment(text);
  131.         }
  132.        
  133.         public override void WriteProcessingInstruction(string name, string text)
  134.         {
  135.             if (this.wrapped == null)
  136.                 this.eventCache.WriteProcessingInstruction(name, text);
  137.             else
  138.                 this.wrapped.WriteProcessingInstruction(name, text);
  139.         }
  140.        
  141.         public override void WriteWhitespace(string ws)
  142.         {
  143.             if (this.wrapped == null)
  144.                 this.eventCache.WriteWhitespace(ws);
  145.             else
  146.                 this.wrapped.WriteWhitespace(ws);
  147.         }
  148.        
  149.         public override void WriteString(string text)
  150.         {
  151.             if (TextBlockCreatesWriter(text))
  152.                 this.wrapped.WriteString(text);
  153.             else
  154.                 this.eventCache.WriteString(text);
  155.         }
  156.        
  157.         public override void WriteChars(char[] buffer, int index, int count)
  158.         {
  159.             WriteString(new string(buffer, index, count));
  160.         }
  161.        
  162.         public override void WriteRaw(char[] buffer, int index, int count)
  163.         {
  164.             WriteRaw(new string(buffer, index, count));
  165.         }
  166.        
  167.         public override void WriteRaw(string data)
  168.         {
  169.             if (TextBlockCreatesWriter(data))
  170.                 this.wrapped.WriteRaw(data);
  171.             else
  172.                 this.eventCache.WriteRaw(data);
  173.         }
  174.        
  175.         public override void WriteEntityRef(string name)
  176.         {
  177.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  178.             this.wrapped.WriteEntityRef(name);
  179.         }
  180.        
  181.         public override void WriteCharEntity(char ch)
  182.         {
  183.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  184.             this.wrapped.WriteCharEntity(ch);
  185.         }
  186.        
  187.         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  188.         {
  189.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  190.             this.wrapped.WriteSurrogateCharEntity(lowChar, highChar);
  191.         }
  192.        
  193.         public override void WriteBase64(byte[] buffer, int index, int count)
  194.         {
  195.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  196.             this.wrapped.WriteBase64(buffer, index, count);
  197.         }
  198.        
  199.         public override void WriteBinHex(byte[] buffer, int index, int count)
  200.         {
  201.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  202.             this.wrapped.WriteBinHex(buffer, index, count);
  203.         }
  204.        
  205.         public override void Close()
  206.         {
  207.             // Flush any cached events to an Xml writer
  208.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  209.             this.wrapped.Close();
  210.         }
  211.        
  212.         public override void Flush()
  213.         {
  214.             // Flush any cached events to an Xml writer
  215.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  216.             this.wrapped.Flush();
  217.         }
  218.        
  219.         public override void WriteValue(object value)
  220.         {
  221.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  222.             this.wrapped.WriteValue(value);
  223.         }
  224.        
  225.         public override void WriteValue(string value)
  226.         {
  227.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  228.             this.wrapped.WriteValue(value);
  229.         }
  230.        
  231.         public override void WriteValue(bool value)
  232.         {
  233.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  234.             this.wrapped.WriteValue(value);
  235.         }
  236.        
  237.         public override void WriteValue(DateTime value)
  238.         {
  239.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  240.             this.wrapped.WriteValue(value);
  241.         }
  242.        
  243.         public override void WriteValue(double value)
  244.         {
  245.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  246.             this.wrapped.WriteValue(value);
  247.         }
  248.        
  249.         public override void WriteValue(float value)
  250.         {
  251.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  252.             this.wrapped.WriteValue(value);
  253.         }
  254.        
  255.         public override void WriteValue(decimal value)
  256.         {
  257.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  258.             this.wrapped.WriteValue(value);
  259.         }
  260.        
  261.         public override void WriteValue(int value)
  262.         {
  263.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  264.             this.wrapped.WriteValue(value);
  265.         }
  266.        
  267.         public override void WriteValue(long value)
  268.         {
  269.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  270.             this.wrapped.WriteValue(value);
  271.         }
  272.        
  273.         //-----------------------------------------------
  274.         // XmlRawWriter interface
  275.         //-----------------------------------------------
  276.        
  277.         internal override IXmlNamespaceResolver NamespaceResolver {
  278.             get { return this.resolver; }
  279.             set {
  280.                 this.resolver = value;
  281.                
  282.                 if (this.wrapped == null)
  283.                     this.eventCache.NamespaceResolver = value;
  284.                 else
  285.                     this.wrapped.NamespaceResolver = value;
  286.             }
  287.         }
  288.        
  289.         internal override void WriteXmlDeclaration(XmlStandalone standalone)
  290.         {
  291.             // Forces xml writer to be created
  292.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  293.             this.wrapped.WriteXmlDeclaration(standalone);
  294.         }
  295.        
  296.         internal override void WriteXmlDeclaration(string xmldecl)
  297.         {
  298.             // Forces xml writer to be created
  299.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  300.             this.wrapped.WriteXmlDeclaration(xmldecl);
  301.         }
  302.        
  303.         internal override void StartElementContent()
  304.         {
  305.             Debug.Assert(this.wrapped != null);
  306.             this.wrapped.StartElementContent();
  307.         }
  308.        
  309.         internal override void WriteEndElement(string prefix, string localName, string ns)
  310.         {
  311.             Debug.Assert(this.wrapped != null);
  312.             this.wrapped.WriteEndElement(prefix, localName, ns);
  313.         }
  314.        
  315.         internal override void WriteFullEndElement(string prefix, string localName, string ns)
  316.         {
  317.             Debug.Assert(this.wrapped != null);
  318.             this.wrapped.WriteFullEndElement(prefix, localName, ns);
  319.         }
  320.        
  321.         internal override void WriteNamespaceDeclaration(string prefix, string ns)
  322.         {
  323.             EnsureWrappedWriter(XmlOutputMethod.Xml);
  324.             this.wrapped.WriteNamespaceDeclaration(prefix, ns);
  325.         }
  326.        
  327.        
  328.         //-----------------------------------------------
  329.         // Helper methods
  330.         //-----------------------------------------------
  331.        
  332.         /// <summary>
  333.         /// Return true if "tagName" == "html" (case-insensitive).
  334.         /// </summary>
  335.         private static bool IsHtmlTag(string tagName)
  336.         {
  337.             if (tagName.Length != 4)
  338.                 return false;
  339.            
  340.             if (tagName[0] != 'H' && tagName[0] != 'h')
  341.                 return false;
  342.            
  343.             if (tagName[1] != 'T' && tagName[1] != 't')
  344.                 return false;
  345.            
  346.             if (tagName[2] != 'M' && tagName[2] != 'm')
  347.                 return false;
  348.            
  349.             if (tagName[3] != 'L' && tagName[3] != 'l')
  350.                 return false;
  351.            
  352.             return true;
  353.         }
  354.        
  355.         /// <summary>
  356.         /// If a wrapped writer has not yet been created, create one.
  357.         /// </summary>
  358.         private void EnsureWrappedWriter(XmlOutputMethod outMethod)
  359.         {
  360.             if (this.wrapped == null)
  361.                 CreateWrappedWriter(outMethod);
  362.         }
  363.        
  364.         /// <summary>
  365.         /// If the specified text consist only of whitespace, then cache the whitespace, as it is not enough to
  366.         /// force the creation of a wrapped writer. Otherwise, create a wrapped writer if one has not yet been
  367.         /// created and return true.
  368.         /// </summary>
  369.         private bool TextBlockCreatesWriter(string textBlock)
  370.         {
  371.             if (this.wrapped == null) {
  372.                 // Whitespace-only text blocks aren't enough to determine Xml vs. Html
  373.                 if (XmlCharType.Instance.IsOnlyWhitespace(textBlock)) {
  374.                     return false;
  375.                 }
  376.                
  377.                 // Non-whitespace text block selects Xml method
  378.                 CreateWrappedWriter(XmlOutputMethod.Xml);
  379.             }
  380.            
  381.             return true;
  382.         }
  383.        
  384.         /// <summary>
  385.         /// Create either the Html or Xml writer and send any cached events to it.
  386.         /// </summary>
  387.         private void CreateWrappedWriter(XmlOutputMethod outMethod)
  388.         {
  389.             Debug.Assert(this.wrapped == null);
  390.            
  391.             // Create either the Xml or Html writer
  392.             this.writerSettings.ReadOnly = false;
  393.             this.writerSettings.OutputMethod = outMethod;
  394.            
  395.             // If Indent was not set by the user, then default to True for Html
  396.             if (outMethod == XmlOutputMethod.Html && this.writerSettings.InternalIndent == TriState.Unknown)
  397.                 this.writerSettings.Indent = true;
  398.            
  399.             this.writerSettings.ReadOnly = true;
  400.            
  401.             if (textWriter != null)
  402.                 this.wrapped = ((XmlWellFormedWriter)XmlWriter.Create(this.textWriter, this.writerSettings)).RawWriter;
  403.             else
  404.                 this.wrapped = ((XmlWellFormedWriter)XmlWriter.Create(this.strm, this.writerSettings)).RawWriter;
  405.            
  406.             // Send cached events to the new writer
  407.             this.eventCache.EndEvents();
  408.             this.eventCache.EventsToWriter(this.wrapped);
  409.            
  410.             // Send OnRemoveWriter event
  411.             if (this.onRemove != null)
  412.                 (this.onRemove)(this.wrapped);
  413.         }
  414.     }
  415. }

Developer Fusion