ASN1Set.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.asn1;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.util.Enumeration;
6import java.util.Vector;
7
8abstract public class ASN1Set
9    extends ASN1Primitive
10{
11    private Vector set = new Vector();
12    private boolean isSorted = false;
13
14    /**
15     * return an ASN1Set from the given object.
16     *
17     * @param obj the object we want converted.
18     * @exception IllegalArgumentException if the object cannot be converted.
19     */
20    public static ASN1Set getInstance(
21        Object  obj)
22    {
23        if (obj == null || obj instanceof ASN1Set)
24        {
25            return (ASN1Set)obj;
26        }
27        else if (obj instanceof ASN1SetParser)
28        {
29            return ASN1Set.getInstance(((ASN1SetParser)obj).toASN1Primitive());
30        }
31        else if (obj instanceof byte[])
32        {
33            try
34            {
35                return ASN1Set.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
36            }
37            catch (IOException e)
38            {
39                throw new IllegalArgumentException("failed to construct set from byte[]: " + e.getMessage());
40            }
41        }
42        else if (obj instanceof ASN1Encodable)
43        {
44            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
45
46            if (primitive instanceof ASN1Set)
47            {
48                return (ASN1Set)primitive;
49            }
50        }
51
52        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
53    }
54
55    /**
56     * Return an ASN1 set from a tagged object. There is a special
57     * case here, if an object appears to have been explicitly tagged on
58     * reading but we were expecting it to be implicitly tagged in the
59     * normal course of events it indicates that we lost the surrounding
60     * set - so we need to add it back (this will happen if the tagged
61     * object is a sequence that contains other sequences). If you are
62     * dealing with implicitly tagged sets you really <b>should</b>
63     * be using this method.
64     *
65     * @param obj the tagged object.
66     * @param explicit true if the object is meant to be explicitly tagged
67     *          false otherwise.
68     * @exception IllegalArgumentException if the tagged object cannot
69     *          be converted.
70     */
71    public static ASN1Set getInstance(
72        ASN1TaggedObject    obj,
73        boolean             explicit)
74    {
75        if (explicit)
76        {
77            if (!obj.isExplicit())
78            {
79                throw new IllegalArgumentException("object implicit - explicit expected.");
80            }
81
82            return (ASN1Set)obj.getObject();
83        }
84        else
85        {
86            //
87            // constructed object which appears to be explicitly tagged
88            // and it's really implicit means we have to add the
89            // surrounding set.
90            //
91            if (obj.isExplicit())
92            {
93                if (obj instanceof BERTaggedObject)
94                {
95                    return new BERSet(obj.getObject());
96                }
97                else
98                {
99                    return new DLSet(obj.getObject());
100                }
101            }
102            else
103            {
104                if (obj.getObject() instanceof ASN1Set)
105                {
106                    return (ASN1Set)obj.getObject();
107                }
108
109                //
110                // in this case the parser returns a sequence, convert it
111                // into a set.
112                //
113
114
115                ASN1EncodableVector v = new ASN1EncodableVector();
116
117                if (obj.getObject() instanceof ASN1Sequence)
118                {
119                    ASN1Sequence s = (ASN1Sequence)obj.getObject();
120
121                    if (obj instanceof BERTaggedObject)
122                    {
123                        return new BERSet(s.toArray());
124                    }
125                    else
126                    {
127                        return new DLSet(s.toArray());
128                    }
129                }
130            }
131        }
132
133        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
134    }
135
136    protected ASN1Set()
137    {
138    }
139
140    /**
141     * create a sequence containing one object
142     */
143    protected ASN1Set(
144        ASN1Encodable obj)
145    {
146        set.addElement(obj);
147    }
148
149    /**
150     * create a sequence containing a vector of objects.
151     */
152    protected ASN1Set(
153        ASN1EncodableVector v,
154        boolean                  doSort)
155    {
156        for (int i = 0; i != v.size(); i++)
157        {
158            set.addElement(v.get(i));
159        }
160
161        if (doSort)
162        {
163            this.sort();
164        }
165    }
166
167    /**
168     * create a sequence containing a vector of objects.
169     */
170    protected ASN1Set(
171        ASN1Encodable[]   array,
172        boolean doSort)
173    {
174        for (int i = 0; i != array.length; i++)
175        {
176            set.addElement(array[i]);
177        }
178
179        if (doSort)
180        {
181            this.sort();
182        }
183    }
184
185    public Enumeration getObjects()
186    {
187        return set.elements();
188    }
189
190    /**
191     * return the object at the set position indicated by index.
192     *
193     * @param index the set number (starting at zero) of the object
194     * @return the object at the set position indicated by index.
195     */
196    public ASN1Encodable getObjectAt(
197        int index)
198    {
199        return (ASN1Encodable)set.elementAt(index);
200    }
201
202    /**
203     * return the number of objects in this set.
204     *
205     * @return the number of objects in this set.
206     */
207    public int size()
208    {
209        return set.size();
210    }
211
212    public ASN1Encodable[] toArray()
213    {
214        ASN1Encodable[] values = new ASN1Encodable[this.size()];
215
216        for (int i = 0; i != this.size(); i++)
217        {
218            values[i] = this.getObjectAt(i);
219        }
220
221        return values;
222    }
223
224    public ASN1SetParser parser()
225    {
226        final ASN1Set outer = this;
227
228        return new ASN1SetParser()
229        {
230            private final int max = size();
231
232            private int index;
233
234            public ASN1Encodable readObject() throws IOException
235            {
236                if (index == max)
237                {
238                    return null;
239                }
240
241                ASN1Encodable obj = getObjectAt(index++);
242                if (obj instanceof ASN1Sequence)
243                {
244                    return ((ASN1Sequence)obj).parser();
245                }
246                if (obj instanceof ASN1Set)
247                {
248                    return ((ASN1Set)obj).parser();
249                }
250
251                return obj;
252            }
253
254            public ASN1Primitive getLoadedObject()
255            {
256                return outer;
257            }
258
259            public ASN1Primitive toASN1Primitive()
260            {
261                return outer;
262            }
263        };
264    }
265
266    public int hashCode()
267    {
268        Enumeration             e = this.getObjects();
269        int                     hashCode = size();
270
271        while (e.hasMoreElements())
272        {
273            Object o = getNext(e);
274            hashCode *= 17;
275
276            hashCode ^= o.hashCode();
277        }
278
279        return hashCode;
280    }
281
282    ASN1Primitive toDERObject()
283    {
284        if (isSorted)
285        {
286            ASN1Set derSet = new DERSet();
287
288            derSet.set = this.set;
289
290            return derSet;
291        }
292        else
293        {
294            Vector v = new Vector();
295
296            for (int i = 0; i != set.size(); i++)
297            {
298                v.addElement(set.elementAt(i));
299            }
300
301            ASN1Set derSet = new DERSet();
302
303            derSet.set = v;
304
305            derSet.sort();
306
307            return derSet;
308        }
309    }
310
311    ASN1Primitive toDLObject()
312    {
313        ASN1Set derSet = new DLSet();
314
315        derSet.set = this.set;
316
317        return derSet;
318    }
319
320    boolean asn1Equals(
321        ASN1Primitive o)
322    {
323        if (!(o instanceof ASN1Set))
324        {
325            return false;
326        }
327
328        ASN1Set   other = (ASN1Set)o;
329
330        if (this.size() != other.size())
331        {
332            return false;
333        }
334
335        Enumeration s1 = this.getObjects();
336        Enumeration s2 = other.getObjects();
337
338        while (s1.hasMoreElements())
339        {
340            ASN1Encodable obj1 = getNext(s1);
341            ASN1Encodable obj2 = getNext(s2);
342
343            ASN1Primitive o1 = obj1.toASN1Primitive();
344            ASN1Primitive o2 = obj2.toASN1Primitive();
345
346            if (o1 == o2 || o1.equals(o2))
347            {
348                continue;
349            }
350
351            return false;
352        }
353
354        return true;
355    }
356
357    private ASN1Encodable getNext(Enumeration e)
358    {
359        ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
360
361        // unfortunately null was allowed as a substitute for DER null
362        if (encObj == null)
363        {
364            return DERNull.INSTANCE;
365        }
366
367        return encObj;
368    }
369
370    /**
371     * return true if a <= b (arrays are assumed padded with zeros).
372     */
373    private boolean lessThanOrEqual(
374         byte[] a,
375         byte[] b)
376    {
377        int len = Math.min(a.length, b.length);
378        for (int i = 0; i != len; ++i)
379        {
380            if (a[i] != b[i])
381            {
382                return (a[i] & 0xff) < (b[i] & 0xff);
383            }
384        }
385        return len == a.length;
386    }
387
388    private byte[] getEncoded(
389        ASN1Encodable obj)
390    {
391        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
392        ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
393
394        try
395        {
396            aOut.writeObject(obj);
397        }
398        catch (IOException e)
399        {
400            throw new IllegalArgumentException("cannot encode object added to SET");
401        }
402
403        return bOut.toByteArray();
404    }
405
406    protected void sort()
407    {
408        if (!isSorted)
409        {
410            isSorted = true;
411            if (set.size() > 1)
412            {
413                boolean    swapped = true;
414                int        lastSwap = set.size() - 1;
415
416                while (swapped)
417                {
418                    int    index = 0;
419                    int    swapIndex = 0;
420                    byte[] a = getEncoded((ASN1Encodable)set.elementAt(0));
421
422                    swapped = false;
423
424                    while (index != lastSwap)
425                    {
426                        byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1));
427
428                        if (lessThanOrEqual(a, b))
429                        {
430                            a = b;
431                        }
432                        else
433                        {
434                            Object  o = set.elementAt(index);
435
436                            set.setElementAt(set.elementAt(index + 1), index);
437                            set.setElementAt(o, index + 1);
438
439                            swapped = true;
440                            swapIndex = index;
441                        }
442
443                        index++;
444                    }
445
446                    lastSwap = swapIndex;
447                }
448            }
449        }
450    }
451
452    boolean isConstructed()
453    {
454        return true;
455    }
456
457    abstract void encode(ASN1OutputStream out)
458            throws IOException;
459
460    public String toString()
461    {
462        return set.toString();
463    }
464}
465