The Labs \ Source Viewer \ SSCLI \ System.Configuration \ StreamWriterCheckpoint

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlUtilWriter.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.Configuration
  16. {
  17.     using System.Configuration.Internal;
  18.     using System.Collections;
  19.     using System.Collections.Specialized;
  20.     using System.Collections.Generic;
  21.     using System.Configuration;
  22.     using System.Globalization;
  23.     using System.IO;
  24.     using System.Runtime.InteropServices;
  25.     using System.Security;
  26.     using System.Security.Permissions;
  27.     using System.Text;
  28.     using System.Xml;
  29.     using System.Net;
  30.    
  31.     //
  32.     // A utility class for writing XML to a TextWriter.
  33.     //
  34.     // When this class is used to copy an XML document that may include a "<!DOCTYPE" directive,
  35.     // we must track what is written until the "<!DOCTYPE" or first document element is found.
  36.     // This is needed because the XML reader does not give us accurate spacing information
  37.     // for the beginning of the "<!DOCTYPE" element.
  38.     //
  39.     // Note that tracking this information is expensive, as it requires a scan of everything that is written
  40.     // until "<!DOCTYPE" or the first element is found.
  41.     //
  42.     // Note also that this class is used at runtime to copy sections, so performance of all
  43.     // writing functions directly affects application startup time.
  44.     //
  45.     internal class XmlUtilWriter
  46.     {
  47.         private const char SPACE = ' ';
  48.         private const string NL = "\r\n";
  49.        
  50.         private static string SPACES_8;
  51.         private static string SPACES_4;
  52.         private static string SPACES_2;
  53.        
  54.         private TextWriter _writer;
  55.         // the wrapped text writer
  56.         private Stream _baseStream;
  57.         // stream under TextWriter when tracking position
  58.         private bool _trackPosition;
  59.         // should write position be tracked?
  60.         private int _lineNumber;
  61.         // line number
  62.         private int _linePosition;
  63.         // line position
  64.         private bool _isLastLineBlank;
  65.         // is the last line blank?
  66.         private object _lineStartCheckpoint;
  67.         // checkpoint taken at the start of each line
  68.         static XmlUtilWriter()
  69.         {
  70.             SPACES_8 = new string(SPACE, 8);
  71.             SPACES_4 = new string(SPACE, 4);
  72.             SPACES_2 = new string(SPACE, 2);
  73.         }
  74.        
  75.         internal XmlUtilWriter(TextWriter writer, bool trackPosition)
  76.         {
  77.             _writer = writer;
  78.             _trackPosition = trackPosition;
  79.             _lineNumber = 1;
  80.             _linePosition = 1;
  81.             _isLastLineBlank = true;
  82.            
  83.             if (_trackPosition) {
  84.                 _baseStream = ((StreamWriter)_writer).BaseStream;
  85.                 _lineStartCheckpoint = CreateStreamCheckpoint();
  86.             }
  87.         }
  88.        
  89.         internal TextWriter Writer {
  90.             get { return _writer; }
  91.         }
  92.        
  93.         internal bool TrackPosition {
  94.             get { return _trackPosition; }
  95.         }
  96.        
  97.         internal int LineNumber {
  98.             get { return _lineNumber; }
  99.         }
  100.        
  101.         internal int LinePosition {
  102.             get { return _linePosition; }
  103.         }
  104.        
  105.         internal bool IsLastLineBlank {
  106.             get { return _isLastLineBlank; }
  107.         }
  108.        
  109.         //
  110.         // Update the position after the character is written to the stream.
  111.         //
  112.         private void UpdatePosition(char ch)
  113.         {
  114.             switch (ch) {
  115.                 case '\r':
  116.                     _lineNumber++;
  117.                     _linePosition = 1;
  118.                     _isLastLineBlank = true;
  119.                     break;
  120.                 case '\n':
  121.                    
  122.                     _lineStartCheckpoint = CreateStreamCheckpoint();
  123.                     break;
  124.                 case SPACE:
  125.                 case '\t':
  126.                    
  127.                     _linePosition++;
  128.                     break;
  129.                 default:
  130.                    
  131.                     _linePosition++;
  132.                     _isLastLineBlank = false;
  133.                     break;
  134.             }
  135.         }
  136.        
  137.         //
  138.         // Write a string to _writer.
  139.         // If we are tracking position, determine the line number and position
  140.         //
  141.         internal int Write(string s)
  142.         {
  143.             if (_trackPosition) {
  144.                 for (int i = 0; i < s.Length; i++) {
  145.                     char ch = s[i];
  146.                     _writer.Write(ch);
  147.                     UpdatePosition(ch);
  148.                 }
  149.             }
  150.             else {
  151.                 _writer.Write(s);
  152.             }
  153.            
  154.            
  155.             return s.Length;
  156.         }
  157.        
  158.         //
  159.         // Write a character to _writer.
  160.         // If we are tracking position, determine the line number and position
  161.         //
  162.         internal int Write(char ch)
  163.         {
  164.             _writer.Write(ch);
  165.             if (_trackPosition) {
  166.                 UpdatePosition(ch);
  167.             }
  168.            
  169.             return 1;
  170.         }
  171.        
  172.         internal void Flush()
  173.         {
  174.             _writer.Flush();
  175.         }
  176.        
  177.         // Escape a text string
  178.         internal int AppendEscapeTextString(string s)
  179.         {
  180.             return AppendEscapeXmlString(s, false, 'A');
  181.         }
  182.        
  183.         // Escape a XML string to preserve XML markup.
  184.         internal int AppendEscapeXmlString(string s, bool inAttribute, char quoteChar)
  185.         {
  186.             int charactersWritten = 0;
  187.             for (int i = 0; i < s.Length; i++) {
  188.                 char ch = s[i];
  189.                
  190.                 bool appendCharEntity = false;
  191.                 string entityRef = null;
  192.                 if ((ch < 32 && ch != '\t' && ch != '\r' && ch != '\n') || (ch > 65533)) {
  193.                     appendCharEntity = true;
  194.                 }
  195.                 else {
  196.                     switch (ch) {
  197.                         case '<':
  198.                             entityRef = "lt";
  199.                             break;
  200.                         case '>':
  201.                            
  202.                             entityRef = "gt";
  203.                             break;
  204.                         case '&':
  205.                            
  206.                             entityRef = "amp";
  207.                             break;
  208.                         case '\'':
  209.                            
  210.                             if (inAttribute && quoteChar == ch) {
  211.                                 entityRef = "apos";
  212.                             }
  213.                             break;
  214.                         case '"':
  215.                            
  216.                             if (inAttribute && quoteChar == ch) {
  217.                                 entityRef = "quot";
  218.                             }
  219.                             break;
  220.                         case '\n':
  221.                         case '\r':
  222.                            
  223.                             appendCharEntity = inAttribute;
  224.                             break;
  225.                         default:
  226.                            
  227.                             break;
  228.                     }
  229.                 }
  230.                
  231.                 if (appendCharEntity) {
  232.                     charactersWritten += AppendCharEntity(ch);
  233.                 }
  234.                 else if (entityRef != null) {
  235.                     charactersWritten += AppendEntityRef(entityRef);
  236.                 }
  237.                 else {
  238.                     charactersWritten += Write(ch);
  239.                 }
  240.             }
  241.            
  242.             return charactersWritten;
  243.         }
  244.        
  245.         internal int AppendEntityRef(string entityRef)
  246.         {
  247.             Write('&');
  248.             Write(entityRef);
  249.             Write(';');
  250.             return entityRef.Length + 2;
  251.         }
  252.        
  253.         internal int AppendCharEntity(char ch)
  254.         {
  255.             string numberToWrite = ((int)ch).ToString("X", CultureInfo.InvariantCulture);
  256.             Write('&');
  257.             Write('#');
  258.             Write('x');
  259.             Write(numberToWrite);
  260.             Write(';');
  261.             return numberToWrite.Length + 4;
  262.         }
  263.        
  264.         internal int AppendCData(string cdata)
  265.         {
  266.             Write("<![CDATA[");
  267.             Write(cdata);
  268.             Write("]]>");
  269.             return cdata.Length + 12;
  270.         }
  271.        
  272.         internal int AppendProcessingInstruction(string name, string value)
  273.         {
  274.             Write("<?");
  275.             Write(name);
  276.             AppendSpace();
  277.             Write(value);
  278.             Write("?>");
  279.             return name.Length + value.Length + 5;
  280.         }
  281.        
  282.         internal int AppendComment(string comment)
  283.         {
  284.             Write("<!--");
  285.             Write(comment);
  286.             Write("-->");
  287.             return comment.Length + 7;
  288.         }
  289.        
  290.         internal int AppendAttributeValue(XmlTextReader reader)
  291.         {
  292.             int charactersWritten = 0;
  293.             char quote = reader.QuoteChar;
  294.            
  295.             //
  296.             // In !DOCTYPE, quote is '\0' for second public attribute.
  297.             // Protect ourselves from writing invalid XML by always
  298.             // supplying a valid quote char.
  299.             //
  300.             if (quote != '"' && quote != '\'') {
  301.                 quote = '"';
  302.             }
  303.            
  304.             charactersWritten += Write(quote);
  305.             while (reader.ReadAttributeValue()) {
  306.                 if (reader.NodeType == XmlNodeType.Text) {
  307.                     charactersWritten += AppendEscapeXmlString(reader.Value, true, quote);
  308.                 }
  309.                 else {
  310.                     charactersWritten += AppendEntityRef(reader.Name);
  311.                 }
  312.             }
  313.            
  314.             charactersWritten += Write(quote);
  315.             return charactersWritten;
  316.         }
  317.        
  318.         // Append whitespace, ensuring there is at least one space.
  319.         internal int AppendRequiredWhiteSpace(int fromLineNumber, int fromLinePosition, int toLineNumber, int toLinePosition)
  320.         {
  321.             int charactersWritten = AppendWhiteSpace(fromLineNumber, fromLinePosition, toLineNumber, toLinePosition);
  322.             if (charactersWritten == 0) {
  323.                 charactersWritten += AppendSpace();
  324.             }
  325.            
  326.             return charactersWritten;
  327.         }
  328.        
  329.        
  330.         // Append whitespce
  331.         internal int AppendWhiteSpace(int fromLineNumber, int fromLinePosition, int toLineNumber, int toLinePosition)
  332.         {
  333.             int charactersWritten = 0;
  334.             while (fromLineNumber++ < toLineNumber) {
  335.                 charactersWritten += AppendNewLine();
  336.                 fromLinePosition = 1;
  337.             }
  338.            
  339.             charactersWritten += AppendSpaces(toLinePosition - fromLinePosition);
  340.             return charactersWritten;
  341.         }
  342.        
  343.         //
  344.         // Append indent
  345.         // linePosition - starting line position
  346.         // indent - number of spaces to indent each unit of depth
  347.         // depth - depth to indent
  348.         // newLine - insert new line before indent?
  349.         //
  350.         internal int AppendIndent(int linePosition, int indent, int depth, bool newLine)
  351.         {
  352.             int charactersWritten = 0;
  353.             if (newLine) {
  354.                 charactersWritten += AppendNewLine();
  355.             }
  356.            
  357.             int c = (linePosition - 1) + (indent * depth);
  358.             charactersWritten += AppendSpaces(c);
  359.             return charactersWritten;
  360.         }
  361.        
  362.         //
  363.         // AppendSpacesToLinePosition
  364.         //
  365.         // Write spaces up to the line position, taking into account the
  366.         // current line position of the writer.
  367.         //
  368.         internal int AppendSpacesToLinePosition(int linePosition)
  369.         {
  370.             Debug.Assert(_trackPosition, "_trackPosition");
  371.            
  372.             if (linePosition <= 0) {
  373.                 return 0;
  374.             }
  375.            
  376.             int delta = linePosition - _linePosition;
  377.             if (delta < 0 && IsLastLineBlank) {
  378.                 SeekToLineStart();
  379.             }
  380.            
  381.             return AppendSpaces(linePosition - _linePosition);
  382.         }
  383.        
  384.         //
  385.         // Append a new line
  386.         //
  387.         internal int AppendNewLine()
  388.         {
  389.             return Write(NL);
  390.         }
  391.        
  392.         //
  393.         // AppendSpaces
  394.         //
  395.         // Write spaces to the writer provided. Since we do not want waste
  396.         // memory by allocating do not use "new String(' ', count)".
  397.         //
  398.         internal int AppendSpaces(int count)
  399.         {
  400.             int c = count;
  401.             while (c > 0) {
  402.                 if (c >= 8) {
  403.                     Write(SPACES_8);
  404.                     c -= 8;
  405.                 }
  406.                 else if (c >= 4) {
  407.                     Write(SPACES_4);
  408.                     c -= 4;
  409.                 }
  410.                 else if (c >= 2) {
  411.                     Write(SPACES_2);
  412.                     c -= 2;
  413.                 }
  414.                 else {
  415.                     Write(SPACE);
  416.                     break;
  417.                 }
  418.             }
  419.            
  420.             return (count > 0) ? count : 0;
  421.         }
  422.        
  423.         //
  424.         // Append a single space character
  425.         //
  426.         internal int AppendSpace()
  427.         {
  428.             return Write(SPACE);
  429.         }
  430.        
  431.         //
  432.         // Reset the stream to the beginning of the current blank line.
  433.         //
  434.         internal void SeekToLineStart()
  435.         {
  436.             Debug.Assert(_isLastLineBlank, "_isLastLineBlank");
  437.             RestoreStreamCheckpoint(_lineStartCheckpoint);
  438.         }
  439.        
  440.         // Create a checkpoint that can be restored with RestoreStreamCheckpoint().
  441.         internal object CreateStreamCheckpoint()
  442.         {
  443.             return new StreamWriterCheckpoint(this);
  444.         }
  445.        
  446.         // Restore the writer state that was recorded with CreateStreamCheckpoint().
  447.         internal void RestoreStreamCheckpoint(object o)
  448.         {
  449.             StreamWriterCheckpoint checkpoint = (StreamWriterCheckpoint)o;
  450.            
  451.             Flush();
  452.            
  453.             _lineNumber = checkpoint._lineNumber;
  454.             _linePosition = checkpoint._linePosition;
  455.             _isLastLineBlank = checkpoint._isLastLineBlank;
  456.            
  457.             _baseStream.Seek(checkpoint._streamPosition, SeekOrigin.Begin);
  458.             _baseStream.SetLength(checkpoint._streamLength);
  459.             _baseStream.Flush();
  460.         }
  461.        
  462.         // Class that contains the state of the writer and its underlying stream.
  463.         private class StreamWriterCheckpoint
  464.         {
  465.             internal int _lineNumber;
  466.             // line number
  467.             internal int _linePosition;
  468.             // line position
  469.             internal bool _isLastLineBlank;
  470.             // is the last line blank?
  471.             internal long _streamLength;
  472.             // length of the stream
  473.             internal long _streamPosition;
  474.             // position of the stream pointer
  475.             internal StreamWriterCheckpoint(XmlUtilWriter writer)
  476.             {
  477.                 writer.Flush();
  478.                 _lineNumber = writer._lineNumber;
  479.                 _linePosition = writer._linePosition;
  480.                 _isLastLineBlank = writer._isLastLineBlank;
  481.                
  482.                 writer._baseStream.Flush();
  483.                 _streamPosition = writer._baseStream.Position;
  484.                 _streamLength = writer._baseStream.Length;
  485.             }
  486.         }
  487.     }
  488. }

Developer Fusion