/*
 * @(#)X500Principal.java	1.7 00/12/19
 *
 * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
 * Copyright 2000 Sun Microsystems, Inc. Tous droits reserves.
 */

package javax.security.auth.x500;

import java.security.Principal;
import sun.security.x509.X500Name;
import java.io.IOException;

/**
 * <p> This class represents an X.500 <code>Principal</code>.
 * <code>X500Principal</code>s are represented by distinguished names such as
 * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".
 *
 * <p>An <code>X500Principal</code> is created from a distinguished name 
 * specified as a <code>String</code> or <code>byte</code> array (the name, 
 * in string or ASN.1 DER encoded form, respectively). This class recognizes
 * and emits string representations of distinguished names in RFC 1779 or 
 * RFC 2253 format.
 *
 * <p>The {@link #getName getName} and {@link #toString toString} methods 
 * canonicalize the distinguished name of the <code>X500Principal</code> 
 * according to the guidelines specified in RFC 1779, with the exception that 
 * this implementation removes extraneous whitespace between the different name 
 * components.  For example, "CN=Duke,  OU=JavaSoft" is canonicalized into 
 * "CN=Duke, OU=JavaSoft".
 *
 * <p>The {@link #getName(String) getName(String)} method canonicalizes the 
 * distinguished name of the <code>X500Principal</code> according to the format
 * specified. If "RFC2253" is specified, the name is canonicalized as specified 
 * in RFC 2253, with the exception that this implementation removes all 
 * extraneous whitespace between the different name components. For example, 
 * "CN=Duke,  OU=JavaSoft" is canonicalized into "CN=Duke,OU=JavaSoft".
 *
 * <p> Principals such as this <code>X500Principal</code>
 * may be associated with a particular <code>Subject</code>
 * to augment that <code>Subject</code> with an additional
 * identity.  Refer to the <code>Subject</code> class for more information
 * on how to achieve this.  Authorization decisions can then be based upon 
 * the Principals associated with a <code>Subject</code>.
 * 
 * @version 1.7, 12/19/00
 * @since 1.4
 * @see java.security.Principal
 * @see javax.security.auth.Subject
 */
public class X500Principal implements Principal, java.io.Serializable {
    /**
     * RFC 1779 String format of Distinguished Names 
     */
    public static final String RFC1779 = "RFC1779";
    /**
     * RFC 2253 String format of Distinguished Names 
     */
    public static final String RFC2253 = "RFC2253";


    private transient String name;

    private transient X500Name thisX500Name;
    
    private transient byte[] encByte = null;

    /** Default constructor */
    protected X500Principal() {
    }

    /**
     * Creates an <code>X500Principal</code> from a string representation of
     * an X.500 distinguished name (ex: 
     * "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").
     * The distinguished name must be specified using the grammar defined in
     * RFC 1779 or RFC 2253 (either format is acceptable). 
     *
     * <p>This constructor recognizes the attribute type keywords defined in 
     * RFC 1779 and RFC 2253 (CN, L, ST, O, OU, C, STREET, DC and UID), as well 
     * as the T, DNQ or DNQUALIFIER, SURNAME, GIVENNAME, INITIALS, GENERATION,
     * and EMAILADDRESS keywords whose OIDs (Object Identifiers) are defined in 
     * RFC 2459. Any other attribute type must be specified as an OID.
     *
     * @param name an X.500 distinguished name in RFC 1779 or RFC 2253 format
     * @exception NullPointerException if the <code>name</code>
     *			is <code>null</code>
     * @exception IllegalArgumentException if the <code>name</code>
     *			is improperly specified
     */
    public X500Principal(String name) {
	if (name == null) {
	    throw new NullPointerException
		(sun.security.util.ResourcesMgr.getString
		("provided null name"));
	}

	try {
	    thisX500Name = new X500Name(name);
	} catch (Exception e) {
	    throw new IllegalArgumentException(e.toString());
	}

	this.name = name;
    }

    /**
     * Creates an <code>X500Principal</code> from a distinguished name in 
     * ASN.1 DER encoded form. The ASN.1 notation for this structure is as 
     * follows.
     * <pre><code>
     * Name ::= CHOICE {
     *   RDNSequence }
     *
     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
     *
     * RelativeDistinguishedName ::=
     *   SET SIZE (1 .. MAX) OF AttributeTypeAndValue
     *
     * AttributeTypeAndValue ::= SEQUENCE {
     *   type     AttributeType,
     *   value    AttributeValue }
     *
     * AttributeType ::= OBJECT IDENTIFIER
     *
     * AttributeValue ::= ANY DEFINED BY AttributeType
     * ....
     * DirectoryString ::= CHOICE {
     *       teletexString           TeletexString (SIZE (1..MAX)),
     *       printableString         PrintableString (SIZE (1..MAX)),
     *       universalString         UniversalString (SIZE (1..MAX)),
     *       utf8String              UTF8String (SIZE (1.. MAX)),
     *       bmpString               BMPString (SIZE (1..MAX)) }
     * </code></pre>
     *
     * @param name a byte array containing the distinguished name in ASN.1 
     * DER encoded form
     * @throws IOException if an encoding error occurs (incorrect form for DN)
     */
    public X500Principal(byte[] name) throws IOException {
	thisX500Name = new X500Name(name);
    }

    /**
     * Returns a string representation of the X.500 distinguished name using
     * the format defined in RFC 1779.
     *
     * <p>This method emits the attribute type keywords defined in 
     * RFC 1779 (CN, L, ST, O, OU, C, STREET), as well as the DC, T, DNQ or 
     * DNQUALIFIER, SURNAME, GIVENNAME, INITIALS, GENERATION,
     * and EMAILADDRESS keywords whose OIDs (Object Identifiers) are defined in 
     * RFC 2459. Any other attribute type is emitted as an OID.
     *
     * @return the distinguished name of this <code>X500Principal</code>
     */
    public String getName() {
	return thisX500Name.getName();
    }

    /**
     * Returns a string representation of the X.500 distinguished name using the
     * specified format. Valid values are "RFC2253" or "RFC1779" (case
     * insensitive). 
     *
     * <p>If "RFC2253" is specified as the format, only the attribute type 
     * keywords defined in RFC 2253 are emitted (CN, L, ST, O, OU, C, STREET,
     * DC, UID). Any other attribute type is emitted as an OID.
     *
     * @return the distinguished name of this <code>X500Principal</code>
     * @throws IllegalArgumentException if the specified format is 
     * invalid
     */
    public String getName(String format) {
	if (format != null) {
	    if (format.equalsIgnoreCase(RFC1779))
		return getName();
	    if (format.equalsIgnoreCase(RFC2253))
		return thisX500Name.getRFC2253Name();
	}
        throw new IllegalArgumentException("invalid format specified");
    }

    /**
     * Returns the distinguished name in ASN.1 DER encoded form. The ASN.1
     * notation for this structure is supplied in the documentation for
     * {@link #X500Principal(byte[] name) X500Principal(byte[] name)}.
     *
     * <p>Note that the byte array returned is cloned to protect against
     * subsequent modifications.
     *
     * @return a byte array containing the distinguished name in ASN.1 DER 
     * encoded form
     * @throws IOException if an encoding error occurs
     */
    public byte[] getEncoded() throws IOException {
	if (encByte == null )
	    encByte = thisX500Name.getEncoded();
	// assert encByte not null
	return ((byte[])(encByte.clone()));
    }

    /**
     * Return a string representation of this <code>X500Principal</code>
     * using the format defined in RFC 1779.
     *
     * @return a string representation of this <code>X500Principal</code>
     */
    public String toString() {
	return thisX500Name.toString();
    }

    /**
     * Compares the specified <code>Object</code> with this 
     * <code>X500Principal</code> for equality.
     *
     * @param o Object to be compared for equality with this
     *		<code>X500Principal</code>
     *
     * @return <code>true</code> if the specified <code>Object</code> is equal 
     *		to this <code>X500Principal</code>, <code>false</code> otherwise
     */
    public boolean equals(Object o) {

        if (this == o)
            return true;

	if (o instanceof X500Principal) {
	    X500Principal that = (X500Principal)o;
	    return this.thisX500Name.equals(that.thisX500Name);
	}
	return false;
    }
 
    /**
     * Return a hash code for this <code>X500Principal</code>.
     *
     * @return a hash code for this <code>X500Principal</code>
     */
    public int hashCode() {
	return thisX500Name.hashCode();
    }

    /**
     * Save the X500Principal object to a stream
     *
     * @serialData this <code>X500Principal</code> is serialized
     *		by writing out its DER-encoded form
     *		(the value of <code>getEncoded</code> is serialized).
     */ 
    private void writeObject(java.io.ObjectOutputStream s)
	throws IOException {
	s.writeObject(this.getEncoded());
    }
    

    /**
     * Reads this object from a stream (i.e., deserializes it)
     */
    private void readObject(java.io.ObjectInputStream s)
	throws java.io.IOException,
	       java.io.NotActiveException,
	       ClassNotFoundException {

	
	//re-create thisX500Name
	thisX500Name = new X500Name((byte[])s.readObject());
    }
}
