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