001package org.apache.commons.ssl.org.bouncycastle.asn1;
002
003import java.io.IOException;
004
005/**
006 * DER TaggedObject - in ASN.1 notation this is any object preceded by
007 * a [n] where n is some number - these are assumed to follow the construction
008 * rules (as with sequences).
009 */
010public class DERTaggedObject
011    extends ASN1TaggedObject
012{
013    private static final byte[] ZERO_BYTES = new byte[0];
014
015    /**
016     * @param explicit true if an explicitly tagged object.
017     * @param tagNo the tag number for this object.
018     * @param obj the tagged object.
019     */
020    public DERTaggedObject(
021        boolean       explicit,
022        int           tagNo,
023        ASN1Encodable obj)
024    {
025        super(explicit, tagNo, obj);
026    }
027
028    public DERTaggedObject(int tagNo, ASN1Encodable encodable)
029    {
030        super(true, tagNo, encodable);
031    }
032
033    boolean isConstructed()
034    {
035        if (!empty)
036        {
037            if (explicit)
038            {
039                return true;
040            }
041            else
042            {
043                ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
044
045                return primitive.isConstructed();
046            }
047        }
048        else
049        {
050            return true;
051        }
052    }
053
054    int encodedLength()
055        throws IOException
056    {
057        if (!empty)
058        {
059            ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
060            int length = primitive.encodedLength();
061
062            if (explicit)
063            {
064                return StreamUtil.calculateTagLength(tagNo) + StreamUtil.calculateBodyLength(length) + length;
065            }
066            else
067            {
068                // header length already in calculation
069                length = length - 1;
070
071                return StreamUtil.calculateTagLength(tagNo) + length;
072            }
073        }
074        else
075        {
076            return StreamUtil.calculateTagLength(tagNo) + 1;
077        }
078    }
079
080    void encode(
081        ASN1OutputStream out)
082        throws IOException
083    {
084        if (!empty)
085        {
086            ASN1Primitive primitive = obj.toASN1Primitive().toDERObject();
087
088            if (explicit)
089            {
090                out.writeTag(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo);
091                out.writeLength(primitive.encodedLength());
092                out.writeObject(primitive);
093            }
094            else
095            {
096                //
097                // need to mark constructed types...
098                //
099                int flags;
100                if (primitive.isConstructed())
101                {
102                    flags = BERTags.CONSTRUCTED | BERTags.TAGGED;
103                }
104                else
105                {
106                    flags = BERTags.TAGGED;
107                }
108
109                out.writeTag(flags, tagNo);
110                out.writeImplicitObject(primitive);
111            }
112        }
113        else
114        {
115            out.writeEncoded(BERTags.CONSTRUCTED | BERTags.TAGGED, tagNo, ZERO_BYTES);
116        }
117    }
118}