001    package org.apache.commons.ssl.asn1;
002    
003    import 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     */
010    public 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    }