/*
 * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.util;

import java.io.*;


/**
 * Represent an ISO Object Identifier.
 *
 * <P>Object Identifiers are arbitrary length hierarchical identifiers.
 * The individual components are numbers, and they define paths from the
 * root of an ISO-managed identifier space.  You will sometimes see a
 * string name used instead of (or in addition to) the numerical id.
 * These are synonyms for the numerical IDs, but are not widely used
 * since most sites do not know all the requisite strings, while all
 * sites can parse the numeric forms.
 *
 * <P>So for example, JavaSoft has the sole authority to assign the
 * meaning to identifiers below the 1.3.6.1.4.1.42.2.17 node in the
 * hierarchy, and other organizations can easily acquire the ability
 * to assign such unique identifiers.
 *
 *
 * @author David Brownell
 * @author Amit Kapoor
 * @author Hemma Prafullchandra
 */
final public
class ObjectIdentifier implements Serializable
{
    /** use serialVersionUID from JDK 1.1. for interoperability */
    private static final long serialVersionUID = 8697030238860181294L;
    private static final int maxFirstComponent = 2;
    private static final int maxSecondComponent = 39;

    /**
     * Constructs an object identifier from a string.  This string
     * should be of the form 1.23.34.45.56 etc.
     */
    public ObjectIdentifier (String oid) throws IOException
    {
        int ch = '.';
        int     start = 0;
        int end = 0;

        // Calculate length of oid
        componentLen = 0;
        while ((end = oid.indexOf(ch,start)) != -1) {
            start = end + 1;
            componentLen += 1;
        }
        componentLen += 1;
        components = new int[componentLen];

        start = 0;
        int i = 0;
        String comp = null;
        try {
            while ((end = oid.indexOf(ch,start)) != -1) {
                comp = oid.substring(start,end);
                components[i++] = Integer.valueOf(comp).intValue();
                start = end + 1;
            }
            comp = oid.substring(start);
            components[i] = Integer.valueOf(comp).intValue();
        } catch (Exception e) {
            throw new IOException("ObjectIdentifier() -- Invalid format: "
                    + e.toString() /* out:kellinwood ,e */);
        }
        checkValidOid(components, componentLen);
        this.stringForm = oid;
    }

    /**
     * Check if the values make a legal OID. There must be at least 2
     * components and they must be all non-negative. The first component
     * should be 0,1 or 2. When the first component is 0 or 1, the
     * second component should be less than or equal to 39
     *
     * @param values the components that will make the OID
     * @param len the number of components to check. Note that the allocation
     *        size of <code>values</code> may be longer than <code>len</code>.
     *        In this case, only the first <code>len</code> items are checked.
     * @exception IOException if this is not a legal OID
     */
    private void checkValidOid(int[] values, int len) throws IOException {
        if (values == null || len < 2) {
            throw new IOException("ObjectIdentifier() -- " +
                    "Must be at least two oid components ");
        }

        for (int i=0; i<len; i++) {
            if (values[i] < 0) {
                throw new IOException("ObjectIdentifier() -- " +
                        "oid component #" + (i+1) + " must be non-negative ");
            }
        }

        if (values[0] > maxFirstComponent) {
            throw new IOException("ObjectIdentifier() -- " +
                    "First oid component is invalid ");
        }

        if (values[0] < 2 && values[1] > maxSecondComponent) {
            throw new IOException("ObjectIdentifier() -- " +
                    "Second oid component is invalid ");
        }
    }
    /**
     * Constructs an object ID from an array of integers.  This
     * is used to construct constant object IDs.
     */
    public ObjectIdentifier (int values []) throws IOException
    {
        checkValidOid(values, values.length);
        components = values.clone();
        componentLen = values.length;
    }

    /**
     * Constructs an object ID from an ASN.1 encoded input stream.
     * The encoding of the ID in the stream uses "DER", a BER/1 subset.
     * In this case, that means a triple { typeId, length, data }.
     *
     * <P><STRONG>NOTE:</STRONG>  When an exception is thrown, the
     * input stream has not been returned to its "initial" state.
     *
     * @param in DER-encoded data holding an object ID
     * @exception IOException indicates a decoding error
     */
    public ObjectIdentifier (DerInputStream in)
        throws IOException
    {
        byte    type_id;
        int     bufferEnd;

        /*
         * Object IDs are a "universal" type, and their tag needs only
         * one byte of encoding.  Verify that the tag of this datum
         * is that of an object ID.
         *
         * Then get and check the length of the ID's encoding.  We set
         * up so that we can use in.available() to check for the end of
         * this value in the data stream.
         */
        type_id = (byte) in.getByte ();
        if (type_id != DerValue.tag_ObjectId)
            throw new IOException (
                "ObjectIdentifier() -- data isn't an object ID"
                + " (tag = " +  type_id + ")"
                );

        bufferEnd = in.available () - in.getLength () - 1;
        if (bufferEnd < 0)
            throw new IOException (
                "ObjectIdentifier() -- not enough data");

        initFromEncoding (in, bufferEnd);
    }

    /*
     * Build the OID from the rest of a DER input buffer; the tag
     * and length have been removed/verified
     */
    ObjectIdentifier (DerInputBuffer buf) throws IOException
    {
        initFromEncoding (new DerInputStream (buf), 0);
    }

    /**
     * Private constructor for use by newInternal(). Dummy argument
     * to avoid clash with the public constructor.
     */
    private ObjectIdentifier(int[] components, boolean dummy) {
        this.components = components;
        this.componentLen = components.length;
    }

    /**
     * Create a new ObjectIdentifier for internal use. The values are
     * neither checked nor cloned.
     */
    public static ObjectIdentifier newInternal(int[] values) {
        return new ObjectIdentifier(values, true);
    }

    /*
     * Helper function -- get the OID from a stream, after tag and
     * length are verified.
     */
    private void initFromEncoding (DerInputStream in, int bufferEnd)
        throws IOException
    {

        /*
         * Now get the components ("sub IDs") one at a time.  We fill a
         * temporary buffer, resizing it as needed.
         */
        int             component;
        boolean         first_subid = true;

        for (components = new int [allocationQuantum], componentLen = 0;
                in.available () > bufferEnd;
        ) {
            component = getComponent (in);
            if (component < 0) {
                throw new IOException(
                    "ObjectIdentifier() -- " +
                    "component values must be nonnegative");
            }
            if (first_subid) {
                int     X, Y;

                /*
                 * NOTE:  the allocation quantum is large enough that we know
                 * we don't have to reallocate here!
                 */
                if (component < 40)
                    X = 0;
                else if (component < 80)
                    X = 1;
                else
                    X = 2;
                Y = component - ( X * 40);
                components [0] = X;
                components [1] = Y;
                componentLen = 2;

                first_subid = false;

            } else {

                /*
                 * Other components are encoded less exotically.  The only
                 * potential trouble is the need to grow the array.
                 */
                if (componentLen >= components.length) {
                    int         tmp_components [];

                    tmp_components = new int [components.length
                                        + allocationQuantum];
                    System.arraycopy (components, 0, tmp_components, 0,
                            components.length);
                    components = tmp_components;
                }
                components [componentLen++] = component;
            }
        }

        checkValidOid(components, componentLen);

        /*
         * Final sanity check -- if we didn't use exactly the number of bytes
         * specified, something's quite wrong.
         */
        if (in.available () != bufferEnd) {
            throw new IOException (
                    "ObjectIdentifier() -- malformed input data");
        }
    }


    /*
     * n.b. the only public interface is DerOutputStream.putOID()
     */
    void encode (DerOutputStream out) throws IOException
    {
        DerOutputStream bytes = new DerOutputStream ();
        int i;

        // According to ISO X.660, when the 1st component is 0 or 1, the 2nd
        // component is restricted to be less than or equal to 39, thus make
        // it small enough to be encoded into one single byte.
        if (components[0] < 2) {
            bytes.write ((components [0] * 40) + components [1]);
        } else {
            putComponent(bytes, (components [0] * 40) + components [1]);
        }
        for (i = 2; i < componentLen; i++)
            putComponent (bytes, components [i]);

        /*
         * Now that we've constructed the component, encode
         * it in the stream we were given.
         */
        out.write (DerValue.tag_ObjectId, bytes);
    }

    /*
     * Tricky OID component parsing technique ... note that one bit
     * per octet is lost, this returns at most 28 bits of component.
     * Also, notice this parses in big-endian format.
     */
    private static int getComponent (DerInputStream in)
    throws IOException
    {
        int retval, i, tmp;

        for (i = 0, retval = 0; i < 4; i++) {
            retval <<= 7;
            tmp = in.getByte ();
            if (i == 0 && tmp == 0x80) {   // First byte is 0x80, BER
                throw new IOException ("ObjectIdentifier() -- " +
                        "sub component starts with 0x80");
            }
            retval |= (tmp & 0x07f);
            if ((tmp & 0x080) == 0)
                return retval;
        }

        throw new IOException ("ObjectIdentifier() -- component value too big");
    }

    /*
     * Reverse of the above routine.  Notice it needs to emit in
     * big-endian form, so it buffers the output until it's ready.
     * (Minimum length encoding is a DER requirement.)
     */
    private static void putComponent (DerOutputStream out, int val)
    throws IOException
    {
        int     i;
        // TODO: val must be <128*128*128*128 here, otherwise, 4 bytes is not
        // enough to hold it. Will address this later.
        byte    buf [] = new byte [4] ;

        for (i = 0; i < 4; i++) {
            buf [i] = (byte) (val & 0x07f);
            val >>>= 7;
            if (val == 0)
                break;
        }
        for ( ; i > 0; --i)
            out.write (buf [i] | 0x080);
        out.write (buf [0]);
    }

    // XXX this API should probably facilitate the JDK sort utility

    /**
     * Compares this identifier with another, for sorting purposes.
     * An identifier does not precede itself.
     *
     * @param other identifer that may precede this one.
     * @return true iff <em>other</em> precedes this one
     *          in a particular sorting order.
     */
    public boolean precedes (ObjectIdentifier other)
    {
        int             i;

        // shorter IDs go first
        if (other == this || componentLen < other.componentLen)
            return false;
        if (other.componentLen < componentLen)
            return true;

        // for each component, the lesser component goes first
        for (i = 0; i < componentLen; i++) {
            if (other.components [i] < components [i])
                return true;
        }

        // identical IDs don't precede each other
        return false;
    }

    /**
     * @deprecated Use equals((Object)oid)
     */
    @Deprecated
    public boolean equals(ObjectIdentifier other) {
        return equals((Object)other);
    }

    /**
     * Compares this identifier with another, for equality.
     *
     * @return true iff the names are identical.
     */
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ObjectIdentifier == false) {
            return false;
        }
        ObjectIdentifier other = (ObjectIdentifier)obj;
        if (componentLen != other.componentLen) {
            return false;
        }
        for (int i = 0; i < componentLen; i++) {
            if (components[i] != other.components[i]) {
                return false;
            }
        }
        return true;
    }

    public int hashCode() {
        int h = componentLen;
        for (int i = 0; i < componentLen; i++) {
            h += components[i] * 37;
        }
        return h;
    }

    /**
     * Returns a string form of the object ID.  The format is the
     * conventional "dot" notation for such IDs, without any
     * user-friendly descriptive strings, since those strings
     * will not be understood everywhere.
     */
    public String toString() {
        String s = stringForm;
        if (s == null) {
            StringBuffer sb = new StringBuffer(componentLen * 4);
            for (int i = 0; i < componentLen; i++) {
                if (i != 0) {
                    sb.append('.');
                }
                sb.append(components[i]);
            }
            s = sb.toString();
            stringForm = s;
        }
        return s;
    }

    /*
     * To simplify, we assume no individual component of an object ID is
     * larger than 32 bits.  Then we represent the path from the root as
     * an array that's (usually) only filled at the beginning.
     */
    private int         components [];                  // path from root
    private int         componentLen;                   // how much is used.

    private transient volatile String stringForm;

    private static final int allocationQuantum = 5;     // >= 2
}
