001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.IOException;
005    import java.util.Enumeration;
006    import java.util.Vector;
007    
008    abstract 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    }