001package org.apache.commons.ssl.asn1;
002
003import java.io.IOException;
004import java.util.Enumeration;
005import java.util.Vector;
006
007public abstract class ASN1Sequence
008    extends ASN1Object {
009    private Vector seq = new Vector();
010
011    /**
012     * return an ASN1Sequence from the given object.
013     *
014     * @param obj the object we want converted.
015     * @throws IllegalArgumentException if the object cannot be converted.
016     */
017    public static ASN1Sequence getInstance(
018        Object obj) {
019        if (obj == null || obj instanceof ASN1Sequence) {
020            return (ASN1Sequence) obj;
021        }
022
023        throw new IllegalArgumentException("unknown object in getInstance");
024    }
025
026    /**
027     * Return an ASN1 sequence from a tagged object. There is a special
028     * case here, if an object appears to have been explicitly tagged on
029     * reading but we were expecting it to be implictly tagged in the
030     * normal course of events it indicates that we lost the surrounding
031     * sequence - so we need to add it back (this will happen if the tagged
032     * object is a sequence that contains other sequences). If you are
033     * dealing with implicitly tagged sequences you really <b>should</b>
034     * be using this method.
035     *
036     * @param obj      the tagged object.
037     * @param explicit true if the object is meant to be explicitly tagged,
038     *                 false otherwise.
039     * @throws IllegalArgumentException if the tagged object cannot
040     *                                  be converted.
041     */
042    public static ASN1Sequence getInstance(
043        ASN1TaggedObject obj,
044        boolean explicit) {
045        if (explicit) {
046            if (!obj.isExplicit()) {
047                throw new IllegalArgumentException("object implicit - explicit expected.");
048            }
049
050            return (ASN1Sequence) obj.getObject();
051        } else {
052            //
053            // constructed object which appears to be explicitly tagged
054            // when it should be implicit means we have to add the
055            // surrounding sequence.
056            //
057            if (obj.isExplicit()) {
058                if (obj instanceof BERTaggedObject) {
059                    return new BERSequence(obj.getObject());
060                } else {
061                    return new DERSequence(obj.getObject());
062                }
063            } else {
064                if (obj.getObject() instanceof ASN1Sequence) {
065                    return (ASN1Sequence) obj.getObject();
066                }
067            }
068        }
069
070        throw new IllegalArgumentException(
071            "unknown object in getInstanceFromTagged");
072    }
073
074    public Enumeration getObjects() {
075        return seq.elements();
076    }
077
078    public ASN1SequenceParser parser() {
079        final ASN1Sequence outer = this;
080
081        return new ASN1SequenceParser() {
082            private final int max = size();
083
084            private int index;
085
086            public DEREncodable readObject() throws IOException {
087                if (index == max) {
088                    return null;
089                }
090
091                DEREncodable obj = getObjectAt(index++);
092                if (obj instanceof ASN1Sequence) {
093                    return ((ASN1Sequence) obj).parser();
094                }
095                if (obj instanceof ASN1Set) {
096                    return ((ASN1Set) obj).parser();
097                }
098
099                return obj;
100            }
101
102            public DERObject getDERObject() {
103                return outer;
104            }
105        };
106    }
107
108    /**
109     * return the object at the sequence postion indicated by index.
110     *
111     * @param index the sequence number (starting at zero) of the object
112     * @return the object at the sequence postion indicated by index.
113     */
114    public DEREncodable getObjectAt(
115        int index) {
116        return (DEREncodable) seq.elementAt(index);
117    }
118
119    /**
120     * return the number of objects in this sequence.
121     *
122     * @return the number of objects in this sequence.
123     */
124    public int size() {
125        return seq.size();
126    }
127
128    public int hashCode() {
129        Enumeration e = this.getObjects();
130        int hashCode = 0;
131
132        while (e.hasMoreElements()) {
133            Object o = e.nextElement();
134
135            if (o != null) {
136                hashCode ^= o.hashCode();
137            }
138        }
139
140        return hashCode;
141    }
142
143    boolean asn1Equals(
144        DERObject o) {
145        if (!(o instanceof ASN1Sequence)) {
146            return false;
147        }
148
149        ASN1Sequence other = (ASN1Sequence) o;
150
151        if (this.size() != other.size()) {
152            return false;
153        }
154
155        Enumeration s1 = this.getObjects();
156        Enumeration s2 = other.getObjects();
157
158        while (s1.hasMoreElements()) {
159            DERObject o1 = ((DEREncodable) s1.nextElement()).getDERObject();
160            DERObject o2 = ((DEREncodable) s2.nextElement()).getDERObject();
161
162            if (o1 == o2 || (o1 != null && o1.equals(o2))) {
163                continue;
164            }
165
166            return false;
167        }
168
169        return true;
170    }
171
172    protected void addObject(
173        DEREncodable obj) {
174        seq.addElement(obj);
175    }
176
177    abstract void encode(DEROutputStream out)
178        throws IOException;
179
180    public String toString() {
181        return seq.toString();
182    }
183}