001package org.apache.commons.ssl.asn1;
002
003import java.io.IOException;
004
005/**
006 * ASN.1 TaggedObject - in ASN.1 nottation this is any object proceeded by
007 * a [n] where n is some number - these are assume to follow the construction
008 * rules (as with sequences).
009 */
010public abstract class ASN1TaggedObject
011    extends ASN1Object
012    implements ASN1TaggedObjectParser {
013    int tagNo;
014    boolean empty = false;
015    boolean explicit = true;
016    DEREncodable obj = null;
017
018    static public ASN1TaggedObject getInstance(
019        ASN1TaggedObject obj,
020        boolean explicit) {
021        if (explicit) {
022            return (ASN1TaggedObject) obj.getObject();
023        }
024
025        throw new IllegalArgumentException("implicitly tagged tagged object");
026    }
027
028    static public ASN1TaggedObject getInstance(
029        Object obj) {
030        if (obj == null || obj instanceof ASN1TaggedObject) {
031            return (ASN1TaggedObject) obj;
032        }
033
034        throw new IllegalArgumentException("unknown object in getInstance");
035    }
036
037    /**
038     * Create a tagged object in the explicit style.
039     *
040     * @param tagNo the tag number for this object.
041     * @param obj   the tagged object.
042     */
043    public ASN1TaggedObject(
044        int tagNo,
045        DEREncodable obj) {
046        this.explicit = true;
047        this.tagNo = tagNo;
048        this.obj = obj;
049    }
050
051    /**
052     * Create a tagged object with the style given by the value of explicit.
053     * <p>
054     * If the object implements ASN1Choice the tag style will always be changed
055     * to explicit in accordance with the ASN.1 encoding rules.
056     * </p>
057     *
058     * @param explicit true if the object is explicitly tagged.
059     * @param tagNo    the tag number for this object.
060     * @param obj      the tagged object.
061     */
062    public ASN1TaggedObject(
063        boolean explicit,
064        int tagNo,
065        DEREncodable obj) {
066        if (obj instanceof ASN1Choice) {
067            this.explicit = true;
068        } else {
069            this.explicit = explicit;
070        }
071
072        this.tagNo = tagNo;
073        this.obj = obj;
074    }
075
076    boolean asn1Equals(
077        DERObject o) {
078        if (!(o instanceof ASN1TaggedObject)) {
079            return false;
080        }
081
082        ASN1TaggedObject other = (ASN1TaggedObject) o;
083
084        if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit) {
085            return false;
086        }
087
088        if (obj == null) {
089            if (other.obj != null) {
090                return false;
091            }
092        } else {
093            if (!(obj.getDERObject().equals(other.obj.getDERObject()))) {
094                return false;
095            }
096        }
097
098        return true;
099    }
100
101    public int hashCode() {
102        int code = tagNo;
103
104        if (obj != null) {
105            code ^= obj.hashCode();
106        }
107
108        return code;
109    }
110
111    public int getTagNo() {
112        return tagNo;
113    }
114
115    /**
116     * return whether or not the object may be explicitly tagged.
117     * <p/>
118     * Note: if the object has been read from an input stream, the only
119     * time you can be sure if isExplicit is returning the true state of
120     * affairs is if it returns false. An implicitly tagged object may appear
121     * to be explicitly tagged, so you need to understand the context under
122     * which the reading was done as well, see getObject below.
123     */
124    public boolean isExplicit() {
125        return explicit;
126    }
127
128    public boolean isEmpty() {
129        return empty;
130    }
131
132    /**
133     * return whatever was following the tag.
134     * <p/>
135     * Note: tagged objects are generally context dependent if you're
136     * trying to extract a tagged object you should be going via the
137     * appropriate getInstance method.
138     */
139    public DERObject getObject() {
140        if (obj != null) {
141            return obj.getDERObject();
142        }
143
144        return null;
145    }
146
147    /**
148     * Return the object held in this tagged object as a parser assuming it has
149     * the type of the passed in tag. If the object doesn't have a parser
150     * associated with it, the base object is returned.
151     */
152    public DEREncodable getObjectParser(
153        int tag,
154        boolean isExplicit) {
155        switch (tag) {
156            case DERTags.SET:
157                return ASN1Set.getInstance(this, isExplicit).parser();
158            case DERTags.SEQUENCE:
159                return ASN1Sequence.getInstance(this, isExplicit).parser();
160            case DERTags.OCTET_STRING:
161                return ASN1OctetString.getInstance(this, isExplicit).parser();
162        }
163
164        if (isExplicit) {
165            return getObject();
166        }
167
168        throw new RuntimeException("implicit tagging not implemented for tag: " + tag);
169    }
170
171    abstract void encode(DEROutputStream out)
172        throws IOException;
173
174    public String toString() {
175        return "[" + tagNo + "]" + obj;
176    }
177}