001package org.apache.commons.ssl.asn1;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005import java.util.Enumeration;
006import java.util.Vector;
007
008abstract public class ASN1Set
009    extends ASN1Object {
010    protected Vector set = new Vector();
011
012    /**
013     * return an ASN1Set from the given object.
014     *
015     * @param obj the object we want converted.
016     * @throws IllegalArgumentException if the object cannot be converted.
017     */
018    public static ASN1Set getInstance(
019        Object obj) {
020        if (obj == null || obj instanceof ASN1Set) {
021            return (ASN1Set) obj;
022        }
023
024        throw new IllegalArgumentException("unknown object in getInstance");
025    }
026
027    /**
028     * Return an ASN1 set from a tagged object. There is a special
029     * case here, if an object appears to have been explicitly tagged on
030     * reading but we were expecting it to be implictly tagged in the
031     * normal course of events it indicates that we lost the surrounding
032     * set - so we need to add it back (this will happen if the tagged
033     * object is a sequence that contains other sequences). If you are
034     * dealing with implicitly tagged sets you really <b>should</b>
035     * be using this method.
036     *
037     * @param obj      the tagged object.
038     * @param explicit true if the object is meant to be explicitly tagged
039     *                 false otherwise.
040     * @throws IllegalArgumentException if the tagged object cannot
041     *                                  be converted.
042     */
043    public static ASN1Set getInstance(
044        ASN1TaggedObject obj,
045        boolean explicit) {
046        if (explicit) {
047            if (!obj.isExplicit()) {
048                throw new IllegalArgumentException("object implicit - explicit expected.");
049            }
050
051            return (ASN1Set) obj.getObject();
052        } else {
053            //
054            // constructed object which appears to be explicitly tagged
055            // and it's really implicit means we have to add the
056            // surrounding sequence.
057            //
058            if (obj.isExplicit()) {
059                ASN1Set set = new DERSet(obj.getObject());
060
061                return set;
062            } else {
063                if (obj.getObject() instanceof ASN1Set) {
064                    return (ASN1Set) obj.getObject();
065                }
066
067                //
068                // in this case the parser returns a sequence, convert it
069                // into a set.
070                //
071                ASN1EncodableVector v = new ASN1EncodableVector();
072
073                if (obj.getObject() instanceof ASN1Sequence) {
074                    ASN1Sequence s = (ASN1Sequence) obj.getObject();
075                    Enumeration e = s.getObjects();
076
077                    while (e.hasMoreElements()) {
078                        v.add((DEREncodable) e.nextElement());
079                    }
080
081                    return new DERSet(v, false);
082                }
083            }
084        }
085
086        throw new IllegalArgumentException(
087            "unknown object in getInstanceFromTagged");
088    }
089
090    public ASN1Set() {
091    }
092
093    public Enumeration getObjects() {
094        return set.elements();
095    }
096
097    /**
098     * return the object at the set postion indicated by index.
099     *
100     * @param index the set number (starting at zero) of the object
101     * @return the object at the set postion indicated by index.
102     */
103    public DEREncodable getObjectAt(
104        int index) {
105        return (DEREncodable) set.elementAt(index);
106    }
107
108    /**
109     * return the number of objects in this set.
110     *
111     * @return the number of objects in this set.
112     */
113    public int size() {
114        return set.size();
115    }
116
117    public ASN1SetParser parser() {
118        final ASN1Set outer = this;
119
120        return new ASN1SetParser() {
121            private final int max = size();
122
123            private int index;
124
125            public DEREncodable readObject() throws IOException {
126                if (index == max) {
127                    return null;
128                }
129
130                DEREncodable obj = getObjectAt(index++);
131                if (obj instanceof ASN1Sequence) {
132                    return ((ASN1Sequence) obj).parser();
133                }
134                if (obj instanceof ASN1Set) {
135                    return ((ASN1Set) obj).parser();
136                }
137
138                return obj;
139            }
140
141            public DERObject getDERObject() {
142                return outer;
143            }
144        };
145    }
146
147    public int hashCode() {
148        Enumeration e = this.getObjects();
149        int hashCode = 0;
150
151        while (e.hasMoreElements()) {
152            hashCode ^= e.nextElement().hashCode();
153        }
154
155        return hashCode;
156    }
157
158    boolean asn1Equals(
159        DERObject o) {
160        if (!(o instanceof ASN1Set)) {
161            return false;
162        }
163
164        ASN1Set other = (ASN1Set) o;
165
166        if (this.size() != other.size()) {
167            return false;
168        }
169
170        Enumeration s1 = this.getObjects();
171        Enumeration s2 = other.getObjects();
172
173        while (s1.hasMoreElements()) {
174            DERObject o1 = ((DEREncodable) s1.nextElement()).getDERObject();
175            DERObject o2 = ((DEREncodable) s2.nextElement()).getDERObject();
176
177            if (o1 == o2 || (o1 != null && o1.equals(o2))) {
178                continue;
179            }
180
181            return false;
182        }
183
184        return true;
185    }
186
187    /** return true if a <= b (arrays are assumed padded with zeros). */
188    private boolean lessThanOrEqual(
189        byte[] a,
190        byte[] b) {
191        if (a.length <= b.length) {
192            for (int i = 0; i != a.length; i++) {
193                int l = a[i] & 0xff;
194                int r = b[i] & 0xff;
195
196                if (r > l) {
197                    return true;
198                } else if (l > r) {
199                    return false;
200                }
201            }
202
203            return true;
204        } else {
205            for (int i = 0; i != b.length; i++) {
206                int l = a[i] & 0xff;
207                int r = b[i] & 0xff;
208
209                if (r > l) {
210                    return true;
211                } else if (l > r) {
212                    return false;
213                }
214            }
215
216            return false;
217        }
218    }
219
220    private byte[] getEncoded(
221        DEREncodable obj) {
222        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
223        ASN1OutputStream aOut = new ASN1OutputStream(bOut);
224
225        try {
226            aOut.writeObject(obj);
227        }
228        catch (IOException e) {
229            throw new IllegalArgumentException("cannot encode object added to SET");
230        }
231
232        return bOut.toByteArray();
233    }
234
235    protected void sort() {
236        if (set.size() > 1) {
237            boolean swapped = true;
238            int lastSwap = set.size() - 1;
239
240            while (swapped) {
241                int index = 0;
242                int swapIndex = 0;
243                byte[] a = getEncoded((DEREncodable) set.elementAt(0));
244
245                swapped = false;
246
247                while (index != lastSwap) {
248                    byte[] b = getEncoded((DEREncodable) set.elementAt(index + 1));
249
250                    if (lessThanOrEqual(a, b)) {
251                        a = b;
252                    } else {
253                        Object o = set.elementAt(index);
254
255                        set.setElementAt(set.elementAt(index + 1), index);
256                        set.setElementAt(o, index + 1);
257
258                        swapped = true;
259                        swapIndex = index;
260                    }
261
262                    index++;
263                }
264
265                lastSwap = swapIndex;
266            }
267        }
268    }
269
270    protected void addObject(
271        DEREncodable obj) {
272        set.addElement(obj);
273    }
274
275    abstract void encode(DEROutputStream out)
276        throws IOException;
277
278    public String toString() {
279        return set.toString();
280    }
281}