001package org.apache.commons.ssl.org.bouncycastle.asn1.x500;
002
003import java.util.Enumeration;
004
005import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Choice;
006import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encodable;
007import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Object;
008import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier;
009import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive;
010import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Sequence;
011import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1TaggedObject;
012import org.apache.commons.ssl.org.bouncycastle.asn1.DERSequence;
013import org.apache.commons.ssl.org.bouncycastle.asn1.x500.style.BCStyle;
014
015/**
016 * <pre>
017 *     Name ::= CHOICE {
018 *                       RDNSequence }
019 *
020 *     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
021 *
022 *     RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
023 *
024 *     AttributeTypeAndValue ::= SEQUENCE {
025 *                                   type  OBJECT IDENTIFIER,
026 *                                   value ANY }
027 * </pre>
028 */
029public class X500Name
030    extends ASN1Object
031    implements ASN1Choice
032{
033    private static X500NameStyle    defaultStyle = BCStyle.INSTANCE;
034
035    private boolean                 isHashCodeCalculated;
036    private int                     hashCodeValue;
037
038    private X500NameStyle style;
039    private RDN[] rdns;
040
041    public X500Name(X500NameStyle style, X500Name name)
042    {
043        this.rdns = name.rdns;
044        this.style = style;
045    }
046
047    /**
048     * Return a X500Name based on the passed in tagged object.
049     * 
050     * @param obj tag object holding name.
051     * @param explicit true if explicitly tagged false otherwise.
052     * @return the X500Name
053     */
054    public static X500Name getInstance(
055        ASN1TaggedObject obj,
056        boolean          explicit)
057    {
058        // must be true as choice item
059        return getInstance(ASN1Sequence.getInstance(obj, true));
060    }
061
062    public static X500Name getInstance(
063        Object  obj)
064    {
065        if (obj instanceof X500Name)
066        {
067            return (X500Name)obj;
068        }
069        else if (obj != null)
070        {
071            return new X500Name(ASN1Sequence.getInstance(obj));
072        }
073
074        return null;
075    }
076
077    public static X500Name getInstance(
078        X500NameStyle style,
079        Object        obj)
080    {
081        if (obj instanceof X500Name)
082        {
083            return getInstance(style, ((X500Name)obj).toASN1Primitive());
084        }
085        else if (obj != null)
086        {
087            return new X500Name(style, ASN1Sequence.getInstance(obj));
088        }
089
090        return null;
091    }
092
093    /**
094     * Constructor from ASN1Sequence
095     *
096     * the principal will be a list of constructed sets, each containing an (OID, String) pair.
097     */
098    private X500Name(
099        ASN1Sequence  seq)
100    {
101        this(defaultStyle, seq);
102    }
103
104    private X500Name(
105        X500NameStyle style,
106        ASN1Sequence  seq)
107    {
108        this.style = style;
109        this.rdns = new RDN[seq.size()];
110
111        int index = 0;
112
113        for (Enumeration e = seq.getObjects(); e.hasMoreElements();)
114        {
115            rdns[index++] = RDN.getInstance(e.nextElement());
116        }
117    }
118
119    public X500Name(
120        RDN[] rDNs)
121    {
122        this(defaultStyle, rDNs);
123    }
124
125    public X500Name(
126        X500NameStyle style,
127        RDN[]         rDNs)
128    {
129        this.rdns = rDNs;
130        this.style = style;
131    }
132
133    public X500Name(
134        String dirName)
135    {
136        this(defaultStyle, dirName);
137    }
138
139    public X500Name(
140        X500NameStyle style,
141        String        dirName)
142    {
143        this(style.fromString(dirName));
144
145        this.style = style;
146    }
147
148    /**
149     * return an array of RDNs in structure order.
150     *
151     * @return an array of RDN objects.
152     */
153    public RDN[] getRDNs()
154    {
155        RDN[] tmp = new RDN[this.rdns.length];
156
157        System.arraycopy(rdns, 0, tmp, 0, tmp.length);
158
159        return tmp;
160    }
161
162    /**
163     * return an array of OIDs contained in the attribute type of each RDN in structure order.
164     *
165     * @return an array, possibly zero length, of ASN1ObjectIdentifiers objects.
166     */
167    public ASN1ObjectIdentifier[] getAttributeTypes()
168    {
169        int   count = 0;
170
171        for (int i = 0; i != rdns.length; i++)
172        {
173            RDN rdn = rdns[i];
174
175            count += rdn.size();
176        }
177
178        ASN1ObjectIdentifier[] res = new ASN1ObjectIdentifier[count];
179
180        count = 0;
181
182        for (int i = 0; i != rdns.length; i++)
183        {
184            RDN rdn = rdns[i];
185
186            if (rdn.isMultiValued())
187            {
188                AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
189                for (int j = 0; j != attr.length; j++)
190                {
191                    res[count++] = attr[j].getType();
192                }
193            }
194            else if (rdn.size() != 0)
195            {
196                res[count++] = rdn.getFirst().getType();
197            }
198        }
199
200        return res;
201    }
202
203    /**
204     * return an array of RDNs containing the attribute type given by OID in structure order.
205     *
206     * @param attributeType the type OID we are looking for.
207     * @return an array, possibly zero length, of RDN objects.
208     */
209    public RDN[] getRDNs(ASN1ObjectIdentifier attributeType)
210    {
211        RDN[] res = new RDN[rdns.length];
212        int   count = 0;
213
214        for (int i = 0; i != rdns.length; i++)
215        {
216            RDN rdn = rdns[i];
217
218            if (rdn.isMultiValued())
219            {
220                AttributeTypeAndValue[] attr = rdn.getTypesAndValues();
221                for (int j = 0; j != attr.length; j++)
222                {
223                    if (attr[j].getType().equals(attributeType))
224                    {
225                        res[count++] = rdn;
226                        break;
227                    }
228                }
229            }
230            else
231            {
232                if (rdn.getFirst().getType().equals(attributeType))
233                {
234                    res[count++] = rdn;
235                }
236            }
237        }
238
239        RDN[] tmp = new RDN[count];
240
241        System.arraycopy(res, 0, tmp, 0, tmp.length);
242
243        return tmp;
244    }
245
246    public ASN1Primitive toASN1Primitive()
247    {
248        return new DERSequence(rdns);
249    }
250
251    public int hashCode()
252    {
253        if (isHashCodeCalculated)
254        {
255            return hashCodeValue;
256        }
257
258        isHashCodeCalculated = true;
259
260        hashCodeValue = style.calculateHashCode(this);
261
262        return hashCodeValue;
263    }
264
265    /**
266     * test for equality - note: case is ignored.
267     */
268    public boolean equals(Object obj)
269    {
270        if (obj == this)
271        {
272            return true;
273        }
274
275        if (!(obj instanceof X500Name || obj instanceof ASN1Sequence))
276        {
277            return false;
278        }
279        
280        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
281
282        if (this.toASN1Primitive().equals(derO))
283        {
284            return true;
285        }
286
287        try
288        {
289            return style.areEqual(this, new X500Name(ASN1Sequence.getInstance(((ASN1Encodable)obj).toASN1Primitive())));
290        }
291        catch (Exception e)
292        {
293            return false;
294        }
295    }
296    
297    public String toString()
298    {
299        return style.toString(this);
300    }
301
302    /**
303     * Set the default style for X500Name construction.
304     *
305     * @param style  an X500NameStyle
306     */
307    public static void setDefaultStyle(X500NameStyle style)
308    {
309        if (style == null)
310        {
311            throw new NullPointerException("cannot set style to null");
312        }
313
314        defaultStyle = style;
315    }
316
317    /**
318     * Return the current default style.
319     *
320     * @return default style for X500Name construction.
321     */
322    public static X500NameStyle getDefaultStyle()
323    {
324        return defaultStyle;
325    }
326}