content: package local.org.bouncycastle.asn1;
  
  import java.io.IOException;
  
  /**
   * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by
   * a [n] where n is some number - these are assumed to follow the construction
   * rules (as with sequences).
   */
  public abstract class ASN1TaggedObject
      extends ASN1Primitive
      implements ASN1TaggedObjectParser
  {
      int             tagNo;
      boolean         empty = false;
      boolean         explicit = true;
      ASN1Encodable obj = null;
  
      static public ASN1TaggedObject getInstance(
          ASN1TaggedObject    obj,
          boolean             explicit)
      {
          if (explicit)
          {
              return (ASN1TaggedObject)obj.getObject();
          }
  
          throw new IllegalArgumentException("implicitly tagged tagged object");
      }
  
      static public ASN1TaggedObject getInstance(
          Object obj) 
      {
          if (obj == null || obj instanceof ASN1TaggedObject) 
          {
                  return (ASN1TaggedObject)obj;
          }
          else if (obj instanceof byte[])
          {
              try
              {
                  return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj));
              }
              catch (IOException e)
              {
                  throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage());
              }
          }
  
          throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
      }
  
      /**
       * Create a tagged object with the style given by the value of explicit.
       * <p>
       * If the object implements ASN1Choice the tag style will always be changed
       * to explicit in accordance with the ASN.1 encoding rules.
       * </p>
       * @param explicit true if the object is explicitly tagged.
       * @param tagNo the tag number for this object.
       * @param obj the tagged object.
       */
      public ASN1TaggedObject(
          boolean         explicit,
          int             tagNo,
          ASN1Encodable   obj)
      {
          if (obj instanceof ASN1Choice)
          {
              this.explicit = true;
          }
          else
          {
              this.explicit = explicit;
          }
          
          this.tagNo = tagNo;
  
          if (this.explicit)
          {
              this.obj = obj;
          }
          else
          {
              ASN1Primitive prim = obj.toASN1Primitive();
  
              if (prim instanceof ASN1Set)
              {
                  ASN1Set s = null;
              }
  
              this.obj = obj;
          }
      }
      
      boolean asn1Equals(
          ASN1Primitive o)
      {
          if (!(o instanceof ASN1TaggedObject))
          {
              return false;
          }
          
          ASN1TaggedObject other = (ASN1TaggedObject)o;
          
          if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit)
          {
              return false;
          }
          
          if(obj == null)
          {
              if (other.obj != null)
              {
                  return false;
              }
          }
          else
          {
              if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive())))
              {
                  return false;
              }
          }
          
          return true;
      }
      
      public int hashCode()
      {
          int code = tagNo;
  
          // TODO: actually this is wrong - the problem is that a re-encoded
          // object may end up with a different hashCode due to implicit
          // tagging. As implicit tagging is ambiguous if a sequence is involved
          // it seems the only correct method for both equals and hashCode is to
          // compare the encodings...
          if (obj != null)
          {
              code ^= obj.hashCode();
          }
  
          return code;
      }
  
      public int getTagNo()
      {
          return tagNo;
      }
  
      /**
       * return whether or not the object may be explicitly tagged. 
       * <p>
       * Note: if the object has been read from an input stream, the only
       * time you can be sure if isExplicit is returning the true state of
       * affairs is if it returns false. An implicitly tagged object may appear
       * to be explicitly tagged, so you need to understand the context under
       * which the reading was done as well, see getObject below.
       */
      public boolean isExplicit()
      {
          return explicit;
      }
  
      public boolean isEmpty()
      {
          return empty;
      }
  
      /**
       * return whatever was following the tag.
       * <p>
       * Note: tagged objects are generally context dependent if you're
       * trying to extract a tagged object you should be going via the
       * appropriate getInstance method.
       */
      public ASN1Primitive getObject()
      {
          if (obj != null)
          {
              return obj.toASN1Primitive();
          }
  
          return null;
      }
  
      /**
       * Return the object held in this tagged object as a parser assuming it has
       * the type of the passed in tag. If the object doesn't have a parser
       * associated with it, the base object is returned.
       */
      public ASN1Encodable getObjectParser(
          int     tag,
          boolean isExplicit)
      {
          switch (tag)
          {
          case BERTags.SET:
              return ASN1Set.getInstance(this, isExplicit).parser();
          case BERTags.SEQUENCE:
              return ASN1Sequence.getInstance(this, isExplicit).parser();
          case BERTags.OCTET_STRING:
              return ASN1OctetString.getInstance(this, isExplicit).parser();
          }
  
          if (isExplicit)
          {
              return getObject();
          }
  
          throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
      }
  
      public ASN1Primitive getLoadedObject()
      {
          return this.toASN1Primitive();
      }
  
      ASN1Primitive toDERObject()
      {
          return new DERTaggedObject(explicit, tagNo, obj);
      }
  
      ASN1Primitive toDLObject()
      {
          return new DLTaggedObject(explicit, tagNo, obj);
      }
  
      abstract void encode(ASN1OutputStream out)
          throws IOException;
  
      public String toString()
      {
          return "[" + tagNo + "]" + obj;
      }
  }
  