1package org.bouncycastle.asn1;
2
3import java.io.IOException;
4import java.util.Enumeration;
5import java.util.Vector;
6
7public abstract class ASN1Sequence
8    extends ASN1Primitive
9{
10    protected Vector seq = new Vector();
11
12    /**
13     * return an ASN1Sequence from the given object.
14     *
15     * @param obj the object we want converted.
16     * @exception IllegalArgumentException if the object cannot be converted.
17     */
18    public static ASN1Sequence getInstance(
19        Object  obj)
20    {
21        if (obj == null || obj instanceof ASN1Sequence)
22        {
23            return (ASN1Sequence)obj;
24        }
25        else if (obj instanceof ASN1SequenceParser)
26        {
27            return ASN1Sequence.getInstance(((ASN1SequenceParser)obj).toASN1Primitive());
28        }
29        else if (obj instanceof byte[])
30        {
31            try
32            {
33                return ASN1Sequence.getInstance(fromByteArray((byte[])obj));
34            }
35            catch (IOException e)
36            {
37                throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
38            }
39        }
40        else if (obj instanceof ASN1Encodable)
41        {
42            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
43
44            if (primitive instanceof ASN1Sequence)
45            {
46                return (ASN1Sequence)primitive;
47            }
48        }
49
50        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
51    }
52
53    /**
54     * Return an ASN1 sequence from a tagged object. There is a special
55     * case here, if an object appears to have been explicitly tagged on
56     * reading but we were expecting it to be implicitly tagged in the
57     * normal course of events it indicates that we lost the surrounding
58     * sequence - so we need to add it back (this will happen if the tagged
59     * object is a sequence that contains other sequences). If you are
60     * dealing with implicitly tagged sequences you really <b>should</b>
61     * be using this method.
62     *
63     * @param obj the tagged object.
64     * @param explicit true if the object is meant to be explicitly tagged,
65     *          false otherwise.
66     * @exception IllegalArgumentException if the tagged object cannot
67     *          be converted.
68     */
69    public static ASN1Sequence getInstance(
70        ASN1TaggedObject    obj,
71        boolean             explicit)
72    {
73        if (explicit)
74        {
75            if (!obj.isExplicit())
76            {
77                throw new IllegalArgumentException("object implicit - explicit expected.");
78            }
79
80            return ASN1Sequence.getInstance(obj.getObject().toASN1Primitive());
81        }
82        else
83        {
84            //
85            // constructed object which appears to be explicitly tagged
86            // when it should be implicit means we have to add the
87            // surrounding sequence.
88            //
89            if (obj.isExplicit())
90            {
91                if (obj instanceof BERTaggedObject)
92                {
93                    return new BERSequence(obj.getObject());
94                }
95                else
96                {
97                    return new DLSequence(obj.getObject());
98                }
99            }
100            else
101            {
102                if (obj.getObject() instanceof ASN1Sequence)
103                {
104                    return (ASN1Sequence)obj.getObject();
105                }
106            }
107        }
108
109        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
110    }
111
112    /**
113     * create an empty sequence
114     */
115    protected ASN1Sequence()
116    {
117    }
118
119    /**
120     * create a sequence containing one object
121     */
122    protected ASN1Sequence(
123        ASN1Encodable obj)
124    {
125        seq.addElement(obj);
126    }
127
128    /**
129     * create a sequence containing a vector of objects.
130     */
131    protected ASN1Sequence(
132        ASN1EncodableVector v)
133    {
134        for (int i = 0; i != v.size(); i++)
135        {
136            seq.addElement(v.get(i));
137        }
138    }
139
140    /**
141     * create a sequence containing a vector of objects.
142     */
143    protected ASN1Sequence(
144        ASN1Encodable[]   array)
145    {
146        for (int i = 0; i != array.length; i++)
147        {
148            seq.addElement(array[i]);
149        }
150    }
151
152    public ASN1Encodable[] toArray()
153    {
154        ASN1Encodable[] values = new ASN1Encodable[this.size()];
155
156        for (int i = 0; i != this.size(); i++)
157        {
158            values[i] = this.getObjectAt(i);
159        }
160
161        return values;
162    }
163
164    public Enumeration getObjects()
165    {
166        return seq.elements();
167    }
168
169    public ASN1SequenceParser parser()
170    {
171        final ASN1Sequence outer = this;
172
173        return new ASN1SequenceParser()
174        {
175            private final int max = size();
176
177            private int index;
178
179            public ASN1Encodable readObject() throws IOException
180            {
181                if (index == max)
182                {
183                    return null;
184                }
185
186                ASN1Encodable obj = getObjectAt(index++);
187                if (obj instanceof ASN1Sequence)
188                {
189                    return ((ASN1Sequence)obj).parser();
190                }
191                if (obj instanceof ASN1Set)
192                {
193                    return ((ASN1Set)obj).parser();
194                }
195
196                return obj;
197            }
198
199            public ASN1Primitive getLoadedObject()
200            {
201                return outer;
202            }
203
204            public ASN1Primitive toASN1Primitive()
205            {
206                return outer;
207            }
208        };
209    }
210
211    /**
212     * return the object at the sequence position indicated by index.
213     *
214     * @param index the sequence number (starting at zero) of the object
215     * @return the object at the sequence position indicated by index.
216     */
217    public ASN1Encodable getObjectAt(
218        int index)
219    {
220        return (ASN1Encodable)seq.elementAt(index);
221    }
222
223    /**
224     * return the number of objects in this sequence.
225     *
226     * @return the number of objects in this sequence.
227     */
228    public int size()
229    {
230        return seq.size();
231    }
232
233    public int hashCode()
234    {
235        Enumeration             e = this.getObjects();
236        int                     hashCode = size();
237
238        while (e.hasMoreElements())
239        {
240            Object o = getNext(e);
241            hashCode *= 17;
242
243            hashCode ^= o.hashCode();
244        }
245
246        return hashCode;
247    }
248
249    boolean asn1Equals(
250        ASN1Primitive o)
251    {
252        if (!(o instanceof ASN1Sequence))
253        {
254            return false;
255        }
256
257        ASN1Sequence   other = (ASN1Sequence)o;
258
259        if (this.size() != other.size())
260        {
261            return false;
262        }
263
264        Enumeration s1 = this.getObjects();
265        Enumeration s2 = other.getObjects();
266
267        while (s1.hasMoreElements())
268        {
269            ASN1Encodable obj1 = getNext(s1);
270            ASN1Encodable obj2 = getNext(s2);
271
272            ASN1Primitive o1 = obj1.toASN1Primitive();
273            ASN1Primitive o2 = obj2.toASN1Primitive();
274
275            if (o1 == o2 || o1.equals(o2))
276            {
277                continue;
278            }
279
280            return false;
281        }
282
283        return true;
284    }
285
286    private ASN1Encodable getNext(Enumeration e)
287    {
288        ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
289
290        return encObj;
291    }
292
293    ASN1Primitive toDERObject()
294    {
295        ASN1Sequence derSeq = new DERSequence();
296
297        derSeq.seq = this.seq;
298
299        return derSeq;
300    }
301
302    ASN1Primitive toDLObject()
303    {
304        ASN1Sequence dlSeq = new DLSequence();
305
306        dlSeq.seq = this.seq;
307
308        return dlSeq;
309    }
310
311    boolean isConstructed()
312    {
313        return true;
314    }
315
316    abstract void encode(ASN1OutputStream out)
317        throws IOException;
318
319    public String toString()
320    {
321        return seq.toString();
322    }
323}
324