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