context: /* --------------------------------------------------------------------------
   * Copyrights
   *
   * Portions created by or assigned to Cursive Systems, Inc. are
   * Copyright (c) 2002-2008 Cursive Systems, Inc.  All Rights Reserved.  Contact
   * information for Cursive Systems, Inc. is available at
   * http://www.cursive.net/.
   *
   * License
   *
   * Jabber-Net is licensed under the LGPL.
   * See LICENSE.txt for details.
   * --------------------------------------------------------------------------*/
  using System;
  using System.Collections;
  using System.Xml;
  using Bedrock.IO;
  using XpNet;
  
  namespace Jabber.Protocol
  {
      /// <summary>
      /// Summary description for AsynchElementStream.
      /// TODO: combine with ElementStream, since there's only one impl now.
      /// </summary>
      public class AsynchElementStream : ElementStream
      {
          private static System.Text.Encoding utf = System.Text.Encoding.UTF8;
  
          private BufferAggregate m_buf   = new BufferAggregate();
          private Encoding m_enc   = new UTF8Encoding();
          private NS m_ns    = new NS();
          private XmlElement m_elem  = null;
          private XmlElement m_root  = null;
          private bool m_cdata = false;
  
          /// <summary>
          /// Create an instance.
          /// </summary>
          public AsynchElementStream()
          {
          }
  
          /// <summary>
          /// Put bytes into parser.  Used by test routines, only, for convenience.
          /// </summary>
          /// <param name="buf"></param>
          public void Push(byte[] buf)
          {
              Push(buf, 0, buf.Length);
          }
  
          /// <summary>
          /// Put bytes into the parser.
          /// </summary>
          /// <param name="buf">The bytes to put into the parse stream</param>
          /// <param name="offset">Offset into buf to start at</param>
          /// <param name="length">Number of bytes to write</param>
          public void Push(byte[] buf, int offset, int length)
          {
              // or assert, really, but this is a little nicer.
              if (length == 0)
                  return;
  
              // No locking is required.  Read() won't get called again
              // until this method returns.  Keep in mind that we're
              // already on a thread in a ThreadPool, which is created
              // and managed by System.IO at the end of the day.
  
              // TODO: only do this copy if we have a partial token at the
              // end of parsing.
              byte[] copy = new byte[length];
              System.Buffer.BlockCopy(buf, offset, copy, 0, length);
              m_buf.Write(copy);
  
              byte[] b = m_buf.GetBuffer();
              int off = 0;
              TOK tok = TOK.END_TAG;
              ContentToken ct = new ContentToken();
  
              try
              {
                  while (off < b.Length)
                  {
  
  
                      if (m_cdata)
                          tok = m_enc.tokenizeCdataSection(b, off, b.Length, ct);
                      else
                          tok = m_enc.tokenizeContent(b, off, b.Length, ct);
  
                      switch (tok)
                      {
                      case TOK.EMPTY_ELEMENT_NO_ATTS:
                      case TOK.EMPTY_ELEMENT_WITH_ATTS:
                          StartTag(b, off, ct, tok);
                          EndTag(b, off, ct, tok);
                          break;
                      case TOK.START_TAG_NO_ATTS:
                      case TOK.START_TAG_WITH_ATTS:
                          StartTag(b, off, ct, tok);
                          break;
                      case TOK.END_TAG:
                          EndTag(b, off, ct, tok);
                          break;
                      case TOK.DATA_CHARS:
                      case TOK.DATA_NEWLINE:
                          AddText(utf.GetString(b, off, ct.TokenEnd - off));
                          break;
                      case TOK.CHAR_REF:
                      case TOK.MAGIC_ENTITY_REF:
                          AddText(new string(new char[] { ct.RefChar1 }));
                          break;
                      case TOK.CHAR_PAIR_REF:
                          AddText(new string(new char[] {ct.RefChar1,
                                                                ct.RefChar2}));
                          break;
                      case TOK.COMMENT:
                          if (m_elem != null)
                          {
                              // <!-- 4
                              //  --> 3
                              int start = off + 4*m_enc.MinBytesPerChar;
                              int end = ct.TokenEnd - off -
                                      7*m_enc.MinBytesPerChar;
                              string text = utf.GetString(b, start, end);
                              m_elem.AppendChild(m_doc.CreateComment(text));
                          }
                          break;
                      case TOK.CDATA_SECT_OPEN:
                          m_cdata = true;
                          break;
                      case TOK.CDATA_SECT_CLOSE:
                          m_cdata = false;
                          break;
                      case TOK.XML_DECL:
                          // thou shalt use UTF8, and XML version 1.
                          // i shall ignore evidence to the contrary...
  
                          // TODO: Throw an exception if these assuptions are
                          // wrong
                          break;
                      case TOK.ENTITY_REF:
                      case TOK.PI:
                          throw new System.NotImplementedException("Token type not implemented: " + tok);
                      }
                      off = ct.TokenEnd;
                      ct.clearAttributes();
                  }
              }
              catch (PartialTokenException)
              {
                  // Console.WriteLine("PartialTokenException: " + System.Text.Encoding.UTF8.GetString(copy));
                  // ignored;
              }
              catch (ExtensibleTokenException)
              {
                  // ignored;
              }
              catch (XpNet.InvalidTokenException e)
              {
                  throw new XMLParseException(e, this, buf, offset, length);
              }
              catch (Exception e)
              {
                  throw new Exception("Unexpected exception", e);
              }
              finally
              {
                  m_buf.Clear(off);
                  ct.clearAttributes();
              }
          }
  
          private void StartTag(byte[] buf, int offset,
                                ContentToken ct, TOK tok)
          {
              int colon;
              string name;
              string prefix;
              Hashtable ht = new Hashtable();
  
              m_ns.PushScope();
  
              // if i have attributes
              if ((tok == TOK.START_TAG_WITH_ATTS) ||
                  (tok == TOK.EMPTY_ELEMENT_WITH_ATTS))
              {
                  int start;
                  int end;
                  string val;
                  for (int i=0; i<ct.getAttributeSpecifiedCount(); i++)
                  {
                      start = ct.getAttributeNameStart(i);
                      end = ct.getAttributeNameEnd(i);
                      name = utf.GetString(buf, start, end - start);
  
                      start = ct.getAttributeValueStart(i);
                      end =  ct.getAttributeValueEnd(i);
                      val = utf.GetString(buf, start, end - start);
  
                      // <foo b='&amp;'/>
                      // <foo b='&amp;amp;'
                      // TODO: if val includes &amp;, it gets double-escaped
                      if (name.StartsWith("xmlns:"))
                      {
                          colon = name.IndexOf(':');
  
gt:                         prefix = name.Substring(colon+1);