package org.bouncycastle.asn1; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; public abstract class ASN1Sequence extends ASN1Object { private Vector seq = new Vector(); /** * return an ASN1Sequence from the given object. * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. */ public static ASN1Sequence getInstance( Object obj) { if (obj == null || obj instanceof ASN1Sequence) { return (ASN1Sequence)obj; } else if (obj instanceof byte[]) { try { return ASN1Sequence.getInstance(ASN1Object.fromByteArray((byte[])obj)); } catch (IOException e) { throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage()); } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); } /** * Return an ASN1 sequence from a tagged object. There is a special * case here, if an object appears to have been explicitly tagged on * reading but we were expecting it to be implicitly tagged in the * normal course of events it indicates that we lost the surrounding * sequence - so we need to add it back (this will happen if the tagged * object is a sequence that contains other sequences). If you are * dealing with implicitly tagged sequences you really <b>should</b> * be using this method. * * @param obj the tagged object. * @param explicit true if the object is meant to be explicitly tagged, * false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. */ public static ASN1Sequence getInstance( ASN1TaggedObject obj, boolean explicit) { if (explicit) { if (!obj.isExplicit()) { throw new IllegalArgumentException("object implicit - explicit expected."); } return (ASN1Sequence)obj.getObject(); } else { // // constructed object which appears to be explicitly tagged // when it should be implicit means we have to add the // surrounding sequence. // if (obj.isExplicit()) { if (obj instanceof BERTaggedObject) { return new BERSequence(obj.getObject()); } else { return new DERSequence(obj.getObject()); } } else { if (obj.getObject() instanceof ASN1Sequence) { return (ASN1Sequence)obj.getObject(); } } } throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); } public Enumeration getObjects() { return seq.elements(); } public ASN1SequenceParser parser() { final ASN1Sequence outer = this; return new ASN1SequenceParser() { private final int max = size(); private int index; public DEREncodable readObject() throws IOException { if (index == max) { return null; } DEREncodable obj = getObjectAt(index++); if (obj instanceof ASN1Sequence) { return ((ASN1Sequence)obj).parser(); } if (obj instanceof ASN1Set) { return ((ASN1Set)obj).parser(); } return obj; } public DERObject getLoadedObject() { return outer; } public DERObject getDERObject() { return outer; } }; } /** * return the object at the sequence position indicated by index. * * @param index the sequence number (starting at zero) of the object * @return the object at the sequence position indicated by index. */ public DEREncodable getObjectAt( int index) { return (DEREncodable)seq.elementAt(index); } /** * return the number of objects in this sequence. * * @return the number of objects in this sequence. */ public int size() { return seq.size(); } public int hashCode() { Enumeration e = this.getObjects(); int hashCode = size(); while (e.hasMoreElements()) { Object o = getNext(e); hashCode *= 17; hashCode ^= o.hashCode(); } return hashCode; } boolean asn1Equals( DERObject o) { if (!(o instanceof ASN1Sequence)) { return false; } ASN1Sequence other = (ASN1Sequence)o; if (this.size() != other.size()) { return false; } Enumeration s1 = this.getObjects(); Enumeration s2 = other.getObjects(); while (s1.hasMoreElements()) { DEREncodable obj1 = getNext(s1); DEREncodable obj2 = getNext(s2); DERObject o1 = obj1.getDERObject(); DERObject o2 = obj2.getDERObject(); if (o1 == o2 || o1.equals(o2)) { continue; } return false; } return true; } private DEREncodable getNext(Enumeration e) { DEREncodable encObj = (DEREncodable)e.nextElement(); // unfortunately null was allowed as a substitute for DER null if (encObj == null) { return DERNull.INSTANCE; } return encObj; } protected void addObject( DEREncodable obj) { seq.addElement(obj); } abstract void encode(DEROutputStream out) throws IOException; public String toString() { return seq.toString(); } }