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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="ReadContentAsBinaryHelper.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.Diagnostics;
  16. namespace System.Xml
  17. {
  18.     internal class ReadContentAsBinaryHelper
  19.     {
  20.         // Private enums
  21.         enum State
  22.         {
  23.             None,
  24.             InReadContent,
  25.             InReadElementContent
  26.         }
  27.        
  28.         // Fields
  29.         XmlReader reader;
  30.         State state;
  31.         int valueOffset;
  32.         bool isEnd;
  33.        
  34.         bool canReadValueChunk;
  35.         char[] valueChunk;
  36.         int valueChunkLength;
  37.        
  38.         IncrementalReadDecoder decoder;
  39.         Base64Decoder base64Decoder;
  40.         BinHexDecoder binHexDecoder;
  41.        
  42.         // Constants
  43.         const int ChunkSize = 256;
  44.        
  45.         // Constructor
  46.         internal ReadContentAsBinaryHelper(XmlReader reader)
  47.         {
  48.             this.reader = reader;
  49.             this.canReadValueChunk = reader.CanReadValueChunk;
  50.            
  51.             if (canReadValueChunk) {
  52.                 valueChunk = new char[ChunkSize];
  53.             }
  54.         }
  55.        
  56.         // Static methods
  57.         static internal ReadContentAsBinaryHelper CreateOrReset(ReadContentAsBinaryHelper helper, XmlReader reader)
  58.         {
  59.             if (helper == null) {
  60.                 return new ReadContentAsBinaryHelper(reader);
  61.             }
  62.             else {
  63.                 helper.Reset();
  64.                 return helper;
  65.             }
  66.         }
  67.        
  68.         // Internal methods
  69.        
  70.         internal int ReadContentAsBase64(byte[] buffer, int index, int count)
  71.         {
  72.             // check arguments
  73.             if (buffer == null) {
  74.                 throw new ArgumentNullException("buffer");
  75.             }
  76.             if (count < 0) {
  77.                 throw new ArgumentOutOfRangeException("count");
  78.             }
  79.             if (index < 0) {
  80.                 throw new ArgumentOutOfRangeException("index");
  81.             }
  82.             if (buffer.Length - index < count) {
  83.                 throw new ArgumentOutOfRangeException("count");
  84.             }
  85.            
  86.             switch (state) {
  87.                 case State.None:
  88.                     if (!reader.CanReadContentAs()) {
  89.                         throw reader.CreateReadContentAsException("ReadContentAsBase64");
  90.                     }
  91.                     if (!Init()) {
  92.                         return 0;
  93.                     }
  94.                     break;
  95.                 case State.InReadContent:
  96.                     // if we have a correct decoder, go read
  97.                     if (decoder == base64Decoder) {
  98.                         // read more binary data
  99.                         return ReadContentAsBinary(buffer, index, count);
  100.                     }
  101.                     break;
  102.                 case State.InReadElementContent:
  103.                     throw new InvalidOperationException(Res.GetString(Res.Xml_MixingBinaryContentMethods));
  104.                     break;
  105.                 default:
  106.                     Debug.Assert(false);
  107.                     return 0;
  108.             }
  109.            
  110.             Debug.Assert(state == State.InReadContent);
  111.            
  112.             // setup base64 decoder
  113.             InitBase64Decoder();
  114.            
  115.             // read more binary data
  116.             return ReadContentAsBinary(buffer, index, count);
  117.         }
  118.        
  119.         internal int ReadContentAsBinHex(byte[] buffer, int index, int count)
  120.         {
  121.             // check arguments
  122.             if (buffer == null) {
  123.                 throw new ArgumentNullException("buffer");
  124.             }
  125.             if (count < 0) {
  126.                 throw new ArgumentOutOfRangeException("count");
  127.             }
  128.             if (index < 0) {
  129.                 throw new ArgumentOutOfRangeException("index");
  130.             }
  131.             if (buffer.Length - index < count) {
  132.                 throw new ArgumentOutOfRangeException("count");
  133.             }
  134.            
  135.             switch (state) {
  136.                 case State.None:
  137.                     if (!reader.CanReadContentAs()) {
  138.                         throw reader.CreateReadContentAsException("ReadContentAsBinHex");
  139.                     }
  140.                     if (!Init()) {
  141.                         return 0;
  142.                     }
  143.                     break;
  144.                 case State.InReadContent:
  145.                     // if we have a correct decoder, go read
  146.                     if (decoder == binHexDecoder) {
  147.                         // read more binary data
  148.                         return ReadContentAsBinary(buffer, index, count);
  149.                     }
  150.                     break;
  151.                 case State.InReadElementContent:
  152.                     throw new InvalidOperationException(Res.GetString(Res.Xml_MixingBinaryContentMethods));
  153.                     break;
  154.                 default:
  155.                     Debug.Assert(false);
  156.                     return 0;
  157.             }
  158.            
  159.             Debug.Assert(state == State.InReadContent);
  160.            
  161.             // setup binhex decoder
  162.             InitBinHexDecoder();
  163.            
  164.             // read more binary data
  165.             return ReadContentAsBinary(buffer, index, count);
  166.         }
  167.        
  168.         internal int ReadElementContentAsBase64(byte[] buffer, int index, int count)
  169.         {
  170.             // check arguments
  171.             if (buffer == null) {
  172.                 throw new ArgumentNullException("buffer");
  173.             }
  174.             if (count < 0) {
  175.                 throw new ArgumentOutOfRangeException("count");
  176.             }
  177.             if (index < 0) {
  178.                 throw new ArgumentOutOfRangeException("index");
  179.             }
  180.             if (buffer.Length - index < count) {
  181.                 throw new ArgumentOutOfRangeException("count");
  182.             }
  183.            
  184.             switch (state) {
  185.                 case State.None:
  186.                     if (reader.NodeType != XmlNodeType.Element) {
  187.                         throw reader.CreateReadElementContentAsException("ReadElementContentAsBase64");
  188.                     }
  189.                     if (!InitOnElement()) {
  190.                         return 0;
  191.                     }
  192.                     break;
  193.                 case State.InReadContent:
  194.                     throw new InvalidOperationException(Res.GetString(Res.Xml_MixingBinaryContentMethods));
  195.                     break;
  196.                 case State.InReadElementContent:
  197.                     // if we have a correct decoder, go read
  198.                     if (decoder == base64Decoder) {
  199.                         // read more binary data
  200.                         return ReadElementContentAsBinary(buffer, index, count);
  201.                     }
  202.                     break;
  203.                 default:
  204.                     Debug.Assert(false);
  205.                     return 0;
  206.             }
  207.            
  208.             Debug.Assert(state == State.InReadElementContent);
  209.            
  210.             // setup base64 decoder
  211.             InitBase64Decoder();
  212.            
  213.             // read more binary data
  214.             return ReadElementContentAsBinary(buffer, index, count);
  215.         }
  216.        
  217.         internal int ReadElementContentAsBinHex(byte[] buffer, int index, int count)
  218.         {
  219.             // check arguments
  220.             if (buffer == null) {
  221.                 throw new ArgumentNullException("buffer");
  222.             }
  223.             if (count < 0) {
  224.                 throw new ArgumentOutOfRangeException("count");
  225.             }
  226.             if (index < 0) {
  227.                 throw new ArgumentOutOfRangeException("index");
  228.             }
  229.             if (buffer.Length - index < count) {
  230.                 throw new ArgumentOutOfRangeException("count");
  231.             }
  232.            
  233.             switch (state) {
  234.                 case State.None:
  235.                     if (reader.NodeType != XmlNodeType.Element) {
  236.                         throw reader.CreateReadElementContentAsException("ReadElementContentAsBinHex");
  237.                     }
  238.                     if (!InitOnElement()) {
  239.                         return 0;
  240.                     }
  241.                     break;
  242.                 case State.InReadContent:
  243.                     throw new InvalidOperationException(Res.GetString(Res.Xml_MixingBinaryContentMethods));
  244.                     break;
  245.                 case State.InReadElementContent:
  246.                     // if we have a correct decoder, go read
  247.                     if (decoder == binHexDecoder) {
  248.                         // read more binary data
  249.                         return ReadElementContentAsBinary(buffer, index, count);
  250.                     }
  251.                     break;
  252.                 default:
  253.                     Debug.Assert(false);
  254.                     return 0;
  255.             }
  256.            
  257.             Debug.Assert(state == State.InReadElementContent);
  258.            
  259.             // setup binhex decoder
  260.             InitBinHexDecoder();
  261.            
  262.             // read more binary data
  263.             return ReadElementContentAsBinary(buffer, index, count);
  264.         }
  265.        
  266.         internal void Finish()
  267.         {
  268.             if (state != State.None) {
  269.                 while (MoveToNextContentNode(true))
  270.                     ;
  271.                 if (state == State.InReadElementContent) {
  272.                     if (reader.NodeType != XmlNodeType.EndElement) {
  273.                         throw new XmlException(Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo);
  274.                     }
  275.                     // move off the EndElement
  276.                     reader.Read();
  277.                 }
  278.             }
  279.             Reset();
  280.         }
  281.        
  282.         internal void Reset()
  283.         {
  284.             state = State.None;
  285.             isEnd = false;
  286.             valueOffset = 0;
  287.         }
  288.        
  289.         // Private methods
  290.         private bool Init()
  291.         {
  292.             // make sure we are on a content node
  293.             if (!MoveToNextContentNode(false)) {
  294.                 return false;
  295.             }
  296.            
  297.             state = State.InReadContent;
  298.             isEnd = false;
  299.             return true;
  300.         }
  301.        
  302.         private bool InitOnElement()
  303.         {
  304.             Debug.Assert(reader.NodeType == XmlNodeType.Element);
  305.             bool isEmpty = reader.IsEmptyElement;
  306.            
  307.             // move to content or off the empty element
  308.             reader.Read();
  309.             if (isEmpty) {
  310.                 return false;
  311.             }
  312.            
  313.             // make sure we are on a content node
  314.             if (!MoveToNextContentNode(false)) {
  315.                 if (reader.NodeType != XmlNodeType.EndElement) {
  316.                     throw new XmlException(Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo);
  317.                 }
  318.                 // move off end element
  319.                 reader.Read();
  320.                 return false;
  321.             }
  322.             state = State.InReadElementContent;
  323.             isEnd = false;
  324.             return true;
  325.         }
  326.        
  327.         private void InitBase64Decoder()
  328.         {
  329.             if (base64Decoder == null) {
  330.                 base64Decoder = new Base64Decoder();
  331.             }
  332.             else {
  333.                 base64Decoder.Reset();
  334.             }
  335.             decoder = base64Decoder;
  336.         }
  337.        
  338.         private void InitBinHexDecoder()
  339.         {
  340.             if (binHexDecoder == null) {
  341.                 binHexDecoder = new BinHexDecoder();
  342.             }
  343.             else {
  344.                 binHexDecoder.Reset();
  345.             }
  346.             decoder = binHexDecoder;
  347.         }
  348.        
  349.         private int ReadContentAsBinary(byte[] buffer, int index, int count)
  350.         {
  351.             Debug.Assert(decoder != null);
  352.            
  353.             if (isEnd) {
  354.                 Reset();
  355.                 return 0;
  356.             }
  357.             decoder.SetNextOutputBuffer(buffer, index, count);
  358.            
  359.             for (;;) {
  360.                 // use streaming ReadValueChunk if the reader supports it
  361.                 if (canReadValueChunk) {
  362.                     for (;;) {
  363.                         if (valueOffset < valueChunkLength) {
  364.                             int decodedCharsCount = decoder.Decode(valueChunk, valueOffset, valueChunkLength - valueOffset);
  365.                             valueOffset += decodedCharsCount;
  366.                         }
  367.                         if (decoder.IsFull) {
  368.                             return decoder.DecodedCount;
  369.                         }
  370.                         Debug.Assert(valueOffset == valueChunkLength);
  371.                         if ((valueChunkLength = reader.ReadValueChunk(valueChunk, 0, ChunkSize)) == 0) {
  372.                             break;
  373.                         }
  374.                         valueOffset = 0;
  375.                     }
  376.                 }
  377.                 else {
  378.                     // read what is reader.Value
  379.                     string value = reader.Value;
  380.                     int decodedCharsCount = decoder.Decode(value, valueOffset, value.Length - valueOffset);
  381.                     valueOffset += decodedCharsCount;
  382.                    
  383.                     if (decoder.IsFull) {
  384.                         return decoder.DecodedCount;
  385.                     }
  386.                 }
  387.                
  388.                 valueOffset = 0;
  389.                
  390.                 // move to next textual node in the element content; throw on sub elements
  391.                 if (!MoveToNextContentNode(true)) {
  392.                     isEnd = true;
  393.                     return decoder.DecodedCount;
  394.                 }
  395.             }
  396.         }
  397.        
  398.         private int ReadElementContentAsBinary(byte[] buffer, int index, int count)
  399.         {
  400.             if (count == 0) {
  401.                 return 0;
  402.             }
  403.             // read binary
  404.             int decoded = ReadContentAsBinary(buffer, index, count);
  405.             if (decoded > 0) {
  406.                 return decoded;
  407.             }
  408.            
  409.             // if 0 bytes returned check if we are on a closing EndElement, throw exception if not
  410.             if (reader.NodeType != XmlNodeType.EndElement) {
  411.                 throw new XmlException(Res.Xml_InvalidNodeType, reader.NodeType.ToString(), reader as IXmlLineInfo);
  412.             }
  413.            
  414.             // move off the EndElement
  415.             reader.Read();
  416.             state = State.None;
  417.             return 0;
  418.         }
  419.        
  420.         bool MoveToNextContentNode(bool moveIfOnContentNode)
  421.         {
  422.             do {
  423.                 switch (reader.NodeType) {
  424.                     case XmlNodeType.Attribute:
  425.                         return !moveIfOnContentNode;
  426.                     case XmlNodeType.Text:
  427.                     case XmlNodeType.Whitespace:
  428.                     case XmlNodeType.SignificantWhitespace:
  429.                     case XmlNodeType.CDATA:
  430.                         if (!moveIfOnContentNode) {
  431.                             return true;
  432.                         }
  433.                         break;
  434.                     case XmlNodeType.ProcessingInstruction:
  435.                     case XmlNodeType.Comment:
  436.                     case XmlNodeType.EndEntity:
  437.                         // skip comments, pis and end entity nodes
  438.                         break;
  439.                     case XmlNodeType.EntityReference:
  440.                         if (reader.CanResolveEntity) {
  441.                             reader.ResolveEntity();
  442.                             break;
  443.                         }
  444.                         goto default;
  445.                         break;
  446.                     default:
  447.                         return false;
  448.                 }
  449.                 moveIfOnContentNode = false;
  450.             }
  451.             while (reader.Read());
  452.             return false;
  453.         }
  454.     }
  455. }

Developer Fusion