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                if (obj.getObject() instanceof ASN1Sequence)
114                {
115                    ASN1Sequence s = (ASN1Sequence)obj.getObject();
116
117                    if (obj instanceof BERTaggedObject)
118                    {
119                        return new BERSet(s.toArray());
120                    }
121                    else
122                    {
123                        return new DLSet(s.toArray());
124                    }
125                }
126            }
127        }
128
129        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
130    }
131
132    protected ASN1Set()
133    {
134    }
135
136    /**
137     * create a sequence containing one object
138     */
139    protected ASN1Set(
140        ASN1Encodable obj)
141    {
142        set.addElement(obj);
143    }
144
145    /**
146     * create a sequence containing a vector of objects.
147     */
148    protected ASN1Set(
149        ASN1EncodableVector v,
150        boolean                  doSort)
151    {
152        for (int i = 0; i != v.size(); i++)
153        {
154            set.addElement(v.get(i));
155        }
156
157        if (doSort)
158        {
159            this.sort();
160        }
161    }
162
163    /**
164     * create a sequence containing a vector of objects.
165     */
166    protected ASN1Set(
167        ASN1Encodable[]   array,
168        boolean doSort)
169    {
170        for (int i = 0; i != array.length; i++)
171        {
172            set.addElement(array[i]);
173        }
174
175        if (doSort)
176        {
177            this.sort();
178        }
179    }
180
181    public Enumeration getObjects()
182    {
183        return set.elements();
184    }
185
186    /**
187     * return the object at the set position indicated by index.
188     *
189     * @param index the set number (starting at zero) of the object
190     * @return the object at the set position indicated by index.
191     */
192    public ASN1Encodable getObjectAt(
193        int index)
194    {
195        return (ASN1Encodable)set.elementAt(index);
196    }
197
198    /**
199     * return the number of objects in this set.
200     *
201     * @return the number of objects in this set.
202     */
203    public int size()
204    {
205        return set.size();
206    }
207
208    public ASN1Encodable[] toArray()
209    {
210        ASN1Encodable[] values = new ASN1Encodable[this.size()];
211
212        for (int i = 0; i != this.size(); i++)
213        {
214            values[i] = this.getObjectAt(i);
215        }
216
217        return values;
218    }
219
220    public ASN1SetParser parser()
221    {
222        final ASN1Set outer = this;
223
224        return new ASN1SetParser()
225        {
226            private final int max = size();
227
228            private int index;
229
230            public ASN1Encodable readObject() throws IOException
231            {
232                if (index == max)
233                {
234                    return null;
235                }
236
237                ASN1Encodable obj = getObjectAt(index++);
238                if (obj instanceof ASN1Sequence)
239                {
240                    return ((ASN1Sequence)obj).parser();
241                }
242                if (obj instanceof ASN1Set)
243                {
244                    return ((ASN1Set)obj).parser();
245                }
246
247                return obj;
248            }
249
250            public ASN1Primitive getLoadedObject()
251            {
252                return outer;
253            }
254
255            public ASN1Primitive toASN1Primitive()
256            {
257                return outer;
258            }
259        };
260    }
261
262    public int hashCode()
263    {
264        Enumeration             e = this.getObjects();
265        int                     hashCode = size();
266
267        while (e.hasMoreElements())
268        {
269            Object o = getNext(e);
270            hashCode *= 17;
271
272            hashCode ^= o.hashCode();
273        }
274
275        return hashCode;
276    }
277
278    ASN1Primitive toDERObject()
279    {
280        if (isSorted)
281        {
282            ASN1Set derSet = new DERSet();
283
284            derSet.set = this.set;
285
286            return derSet;
287        }
288        else
289        {
290            Vector v = new Vector();
291
292            for (int i = 0; i != set.size(); i++)
293            {
294                v.addElement(set.elementAt(i));
295            }
296
297            ASN1Set derSet = new DERSet();
298
299            derSet.set = v;
300
301            derSet.sort();
302
303            return derSet;
304        }
305    }
306
307    ASN1Primitive toDLObject()
308    {
309        ASN1Set derSet = new DLSet();
310
311        derSet.set = this.set;
312
313        return derSet;
314    }
315
316    boolean asn1Equals(
317        ASN1Primitive o)
318    {
319        if (!(o instanceof ASN1Set))
320        {
321            return false;
322        }
323
324        ASN1Set   other = (ASN1Set)o;
325
326        if (this.size() != other.size())
327        {
328            return false;
329        }
330
331        Enumeration s1 = this.getObjects();
332        Enumeration s2 = other.getObjects();
333
334        while (s1.hasMoreElements())
335        {
336            ASN1Encodable obj1 = getNext(s1);
337            ASN1Encodable obj2 = getNext(s2);
338
339            ASN1Primitive o1 = obj1.toASN1Primitive();
340            ASN1Primitive o2 = obj2.toASN1Primitive();
341
342            if (o1 == o2 || o1.equals(o2))
343            {
344                continue;
345            }
346
347            return false;
348        }
349
350        return true;
351    }
352
353    private ASN1Encodable getNext(Enumeration e)
354    {
355        ASN1Encodable encObj = (ASN1Encodable)e.nextElement();
356
357        // unfortunately null was allowed as a substitute for DER null
358        if (encObj == null)
359        {
360            return DERNull.INSTANCE;
361        }
362
363        return encObj;
364    }
365
366    /**
367     * return true if a <= b (arrays are assumed padded with zeros).
368     */
369    private boolean lessThanOrEqual(
370         byte[] a,
371         byte[] b)
372    {
373        int len = Math.min(a.length, b.length);
374        for (int i = 0; i != len; ++i)
375        {
376            if (a[i] != b[i])
377            {
378                return (a[i] & 0xff) < (b[i] & 0xff);
379            }
380        }
381        return len == a.length;
382    }
383
384    private byte[] getEncoded(
385        ASN1Encodable obj)
386    {
387        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
388        ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
389
390        try
391        {
392            aOut.writeObject(obj);
393        }
394        catch (IOException e)
395        {
396            throw new IllegalArgumentException("cannot encode object added to SET");
397        }
398
399        return bOut.toByteArray();
400    }
401
402    protected void sort()
403    {
404        if (!isSorted)
405        {
406            isSorted = true;
407            if (set.size() > 1)
408            {
409                boolean    swapped = true;
410                int        lastSwap = set.size() - 1;
411
412                while (swapped)
413                {
414                    int    index = 0;
415                    int    swapIndex = 0;
416                    byte[] a = getEncoded((ASN1Encodable)set.elementAt(0));
417
418                    swapped = false;
419
420                    while (index != lastSwap)
421                    {
422                        byte[] b = getEncoded((ASN1Encodable)set.elementAt(index + 1));
423
424                        if (lessThanOrEqual(a, b))
425                        {
426                            a = b;
427                        }
428                        else
429                        {
430                            Object  o = set.elementAt(index);
431
432                            set.setElementAt(set.elementAt(index + 1), index);
433                            set.setElementAt(o, index + 1);
434
435                            swapped = true;
436                            swapIndex = index;
437                        }
438
439                        index++;
440                    }
441
442                    lastSwap = swapIndex;
443                }
444            }
445        }
446    }
447
448    boolean isConstructed()
449    {
450        return true;
451    }
452
453    abstract void encode(ASN1OutputStream out)
454            throws IOException;
455
456    public String toString()
457    {
458        return set.toString();
459    }
460}
461