content: package local.org.bouncycastle.asn1;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  public class ASN1StreamParser
  {
      private final InputStream _in;
      private final int         _limit;
      private final byte[][] tmpBuffers;
  
      public ASN1StreamParser(
          InputStream in)
      {
          this(in, StreamUtil.findLimit(in));
      }
  
      public ASN1StreamParser(
          InputStream in,
          int         limit)
      {
          this._in = in;
          this._limit = limit;
  
          this.tmpBuffers = new byte[11][];
      }
  
      public ASN1StreamParser(
          byte[] encoding)
      {
          this(new ByteArrayInputStream(encoding), encoding.length);
      }
  
      ASN1Encodable readIndef(int tagValue) throws IOException
      {
          // Note: INDEF => CONSTRUCTED
  
          // TODO There are other tags that may be constructed (e.g. BIT_STRING)
          switch (tagValue)
          {
              case BERTags.EXTERNAL:
                  return new DERExternalParser(this);
              case BERTags.OCTET_STRING:
                  return new BEROctetStringParser(this);
              case BERTags.SEQUENCE:
                  return new BERSequenceParser(this);
              case BERTags.SET:
                  return new BERSetParser(this);
              default:
                  throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
          }
      }
  
      ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
      {
          if (_in instanceof IndefiniteLengthInputStream)
          {
              if (!constructed)
              {
                  throw new IOException("indefinite length primitive encoding encountered");
              }
              
              return readIndef(tag);
          }
  
          if (constructed)
          {
              switch (tag)
              {
                  case BERTags.SET:
                      return new DERSetParser(this);
                  case BERTags.SEQUENCE:
                      return new DERSequenceParser(this);
                  case BERTags.OCTET_STRING:
                      return new BEROctetStringParser(this);
              }
          }
          else
          {
              switch (tag)
              {
                  case BERTags.SET:
                      throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
                  case BERTags.SEQUENCE:
                      throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
                  case BERTags.OCTET_STRING:
                      return new DEROctetStringParser((DefiniteLengthInputStream)_in);
              }
          }
  
          // TODO ASN1Exception
          throw new RuntimeException("implicit tagging not implemented");
      }
  
      ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
      {
          if (!constructed)
          {
              // Note: !CONSTRUCTED => IMPLICIT
              DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
              return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
          }
  
          ASN1EncodableVector v = readVector();
  
          if (_in instanceof IndefiniteLengthInputStream)
          {
              return v.size() == 1
                  ?   new BERTaggedObject(true, tag, v.get(0))
                  :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
          }
  
          return v.size() == 1
              ?   new DERTaggedObject(true, tag, v.get(0))
              :   new DERTaggedObject(false, tag, DERFactory.createSequence(v));
      }
  
      public ASN1Encodable readObject()
          throws IOException
      {
          int tag = _in.read();
          if (tag == -1)
          {
              return null;
          }
  
          //
          // turn of looking for "00" while we resolve the tag
          //
          set00Check(false);
  
          //
          // calculate tag number
          //
          int tagNo = ASN1InputStream.readTagNumber(_in, tag);
  
          boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
  
          //
          // calculate length
          //
          int length = ASN1InputStream.readLength(_in, _limit);
  
          if (length < 0) // indefinite length method
          {
              if (!isConstructed)
              {
                  throw new IOException("indefinite length primitive encoding encountered");
              }
  
              IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
              ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
  
              if ((tag & BERTags.APPLICATION) != 0)
              {
                  return new BERApplicationSpecificParser(tagNo, sp);
              }
  
              if ((tag & BERTags.TAGGED) != 0)
              {
                  return new BERTaggedObjectParser(true, tagNo, sp);
              }
  
              return sp.readIndef(tagNo);
          }
          else
          {
              DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
  
              if ((tag & BERTags.APPLICATION) != 0)
              {
                  return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
              }
  
              if ((tag & BERTags.TAGGED) != 0)
              {
                  return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
              }
  
              if (isConstructed)
              {
                  // TODO There are other tags that may be constructed (e.g. BIT_STRING)
                  switch (tagNo)
                  {
                      case BERTags.OCTET_STRING:
                          //
                          // yes, people actually do this...
                          //
                          return new BEROctetStringParser(new ASN1StreamParser(defIn));
                      case BERTags.SEQUENCE:
                          return new DERSequenceParser(new ASN1StreamParser(defIn));
                      case BERTags.SET:
                          return new DERSetParser(new ASN1StreamParser(defIn));
                      case BERTags.EXTERNAL:
                          return new DERExternalParser(new ASN1StreamParser(defIn));
                      default:
                          throw new IOException("unknown tag " + tagNo + " encountered");
                  }
              }
  
              // Some primitive encodings can be handled by parsers too...
              switch (tagNo)
              {
                  case BERTags.OCTET_STRING:
                      return new DEROctetStringParser(defIn);
              }
  
              try
              {
                  return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
              }
              catch (IllegalArgumentException e)
              {
                  throw new ASN1Exception("corrupted stream detected", e);
              }
          }
      }
  
      private void set00Check(boolean enabled)
      {
          if (_in instanceof IndefiniteLengthInputStream)
          {
              ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
          }
      }
  
      ASN1EncodableVector readVector() throws IOException
      {
          ASN1EncodableVector v = new ASN1EncodableVector();
  
          ASN1Encodable obj;
          while ((obj = readObject()) != null)
          {
              if (obj instanceof InMemoryRepresentable)
              {
                  v.add(((InMemoryRepresentable)obj).getLoadedObject());
              }
              else
              {
                  v.add(obj.toASN1Primitive());
              }
          }
  
          return v;
      }
  }
  