    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}