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