org.eclipse.jgit.lib.AbbreviatedObjectId.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jgit.lib.AbbreviatedObjectId.java

Source

/*
 * Copyright (C) 2008-2009, Google Inc.
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.eclipse.jgit.lib;

import java.io.Serializable;
import java.text.MessageFormat;

import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.RawParseUtils;

/**
 * A prefix abbreviation of an {@link org.eclipse.jgit.lib.ObjectId}.
 * <p>
 * Sometimes Git produces abbreviated SHA-1 strings, using sufficient leading
 * digits from the ObjectId name to still be unique within the repository the
 * string was generated from. These ids are likely to be unique for a useful
 * period of time, especially if they contain at least 6-10 hex digits.
 * <p>
 * This class converts the hex string into a binary form, to make it more
 * efficient for matching against an object.
 */
public final class AbbreviatedObjectId implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * Test a string of characters to verify it is a hex format.
     * <p>
     * If true the string can be parsed with {@link #fromString(String)}.
     *
     * @param id
     *            the string to test.
     * @return true if the string can converted into an AbbreviatedObjectId.
     */
    public static final boolean isId(String id) {
        if (id.length() < 2 || Constants.OBJECT_ID_STRING_LENGTH < id.length())
            return false;
        try {
            for (int i = 0; i < id.length(); i++)
                RawParseUtils.parseHexInt4((byte) id.charAt(i));
            return true;
        } catch (ArrayIndexOutOfBoundsException e) {
            return false;
        }
    }

    /**
     * Convert an AbbreviatedObjectId from hex characters (US-ASCII).
     *
     * @param buf
     *            the US-ASCII buffer to read from.
     * @param offset
     *            position to read the first character from.
     * @param end
     *            one past the last position to read (<code>end-offset</code> is
     *            the length of the string).
     * @return the converted object id.
     */
    public static final AbbreviatedObjectId fromString(final byte[] buf, final int offset, final int end) {
        if (end - offset > Constants.OBJECT_ID_STRING_LENGTH)
            throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidIdLength,
                    Integer.valueOf(end - offset), Integer.valueOf(Constants.OBJECT_ID_STRING_LENGTH)));
        return fromHexString(buf, offset, end);
    }

    /**
     * Convert an AbbreviatedObjectId from an
     * {@link org.eclipse.jgit.lib.AnyObjectId}.
     * <p>
     * This method copies over all bits of the Id, and is therefore complete
     * (see {@link #isComplete()}).
     *
     * @param id
     *            the {@link org.eclipse.jgit.lib.ObjectId} to convert from.
     * @return the converted object id.
     */
    public static final AbbreviatedObjectId fromObjectId(AnyObjectId id) {
        return new AbbreviatedObjectId(Constants.OBJECT_ID_STRING_LENGTH, id.w1, id.w2, id.w3, id.w4, id.w5);
    }

    /**
     * Convert an AbbreviatedObjectId from hex characters.
     *
     * @param str
     *            the string to read from. Must be &lt;= 40 characters.
     * @return the converted object id.
     */
    public static final AbbreviatedObjectId fromString(String str) {
        if (str.length() > Constants.OBJECT_ID_STRING_LENGTH)
            throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidId, str));
        final byte[] b = Constants.encodeASCII(str);
        return fromHexString(b, 0, b.length);
    }

    private static final AbbreviatedObjectId fromHexString(final byte[] bs, int ptr, final int end) {
        try {
            final int a = hexUInt32(bs, ptr, end);
            final int b = hexUInt32(bs, ptr + 8, end);
            final int c = hexUInt32(bs, ptr + 16, end);
            final int d = hexUInt32(bs, ptr + 24, end);
            final int e = hexUInt32(bs, ptr + 32, end);
            return new AbbreviatedObjectId(end - ptr, a, b, c, d, e);
        } catch (ArrayIndexOutOfBoundsException e1) {
            throw new InvalidObjectIdException(bs, ptr, end - ptr);
        }
    }

    private static final int hexUInt32(final byte[] bs, int p, final int end) {
        if (8 <= end - p)
            return RawParseUtils.parseHexInt32(bs, p);

        int r = 0, n = 0;
        while (n < 8 && p < end) {
            r <<= 4;
            r |= RawParseUtils.parseHexInt4(bs[p++]);
            n++;
        }
        return r << ((8 - n) * 4);
    }

    static int mask(int nibbles, int word, int v) {
        final int b = (word - 1) * 8;
        if (b + 8 <= nibbles) {
            // We have all of the bits required for this word.
            //
            return v;
        }

        if (nibbles <= b) {
            // We have none of the bits required for this word.
            //
            return 0;
        }

        final int s = 32 - (nibbles - b) * 4;
        return (v >>> s) << s;
    }

    /** Number of half-bytes used by this id. */
    final int nibbles;

    final int w1;

    final int w2;

    final int w3;

    final int w4;

    final int w5;

    AbbreviatedObjectId(final int n, final int new_1, final int new_2, final int new_3, final int new_4,
            final int new_5) {
        nibbles = n;
        w1 = new_1;
        w2 = new_2;
        w3 = new_3;
        w4 = new_4;
        w5 = new_5;
    }

    /**
     * Get number of hex digits appearing in this id.
     *
     * @return number of hex digits appearing in this id.
     */
    public int length() {
        return nibbles;
    }

    /**
     * Whether this ObjectId is actually a complete id.
     *
     * @return true if this ObjectId is actually a complete id.
     */
    public boolean isComplete() {
        return length() == Constants.OBJECT_ID_STRING_LENGTH;
    }

    /**
     * A complete ObjectId; null if {@link #isComplete()} is false
     *
     * @return a complete ObjectId; null if {@link #isComplete()} is false
     */
    public ObjectId toObjectId() {
        return isComplete() ? new ObjectId(w1, w2, w3, w4, w5) : null;
    }

    /**
     * Compares this abbreviation to a full object id.
     *
     * @param other
     *            the other object id.
     * @return &lt;0 if this abbreviation names an object that is less than
     *         <code>other</code>; 0 if this abbreviation exactly matches the
     *         first {@link #length()} digits of <code>other.name()</code>;
     *         &gt;0 if this abbreviation names an object that is after
     *         <code>other</code>.
     */
    public final int prefixCompare(AnyObjectId other) {
        int cmp;

        cmp = NB.compareUInt32(w1, mask(1, other.w1));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w2, mask(2, other.w2));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w3, mask(3, other.w3));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w4, mask(4, other.w4));
        if (cmp != 0)
            return cmp;

        return NB.compareUInt32(w5, mask(5, other.w5));
    }

    /**
     * Compare this abbreviation to a network-byte-order ObjectId.
     *
     * @param bs
     *            array containing the other ObjectId in network byte order.
     * @param p
     *            position within {@code bs} to start the compare at. At least
     *            20 bytes, starting at this position are required.
     * @return &lt;0 if this abbreviation names an object that is less than
     *         <code>other</code>; 0 if this abbreviation exactly matches the
     *         first {@link #length()} digits of <code>other.name()</code>;
     *         &gt;0 if this abbreviation names an object that is after
     *         <code>other</code>.
     */
    public final int prefixCompare(byte[] bs, int p) {
        int cmp;

        cmp = NB.compareUInt32(w1, mask(1, NB.decodeInt32(bs, p)));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w2, mask(2, NB.decodeInt32(bs, p + 4)));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w3, mask(3, NB.decodeInt32(bs, p + 8)));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w4, mask(4, NB.decodeInt32(bs, p + 12)));
        if (cmp != 0)
            return cmp;

        return NB.compareUInt32(w5, mask(5, NB.decodeInt32(bs, p + 16)));
    }

    /**
     * Compare this abbreviation to a network-byte-order ObjectId.
     *
     * @param bs
     *            array containing the other ObjectId in network byte order.
     * @param p
     *            position within {@code bs} to start the compare at. At least 5
     *            ints, starting at this position are required.
     * @return &lt;0 if this abbreviation names an object that is less than
     *         <code>other</code>; 0 if this abbreviation exactly matches the
     *         first {@link #length()} digits of <code>other.name()</code>;
     *         &gt;0 if this abbreviation names an object that is after
     *         <code>other</code>.
     */
    public final int prefixCompare(int[] bs, int p) {
        int cmp;

        cmp = NB.compareUInt32(w1, mask(1, bs[p]));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w2, mask(2, bs[p + 1]));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w3, mask(3, bs[p + 2]));
        if (cmp != 0)
            return cmp;

        cmp = NB.compareUInt32(w4, mask(4, bs[p + 3]));
        if (cmp != 0)
            return cmp;

        return NB.compareUInt32(w5, mask(5, bs[p + 4]));
    }

    /**
     * Get value for a fan-out style map, only valid of length &gt;= 2.
     *
     * @return value for a fan-out style map, only valid of length &gt;= 2.
     */
    public final int getFirstByte() {
        return w1 >>> 24;
    }

    private int mask(int word, int v) {
        return mask(nibbles, word, v);
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        return w1;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object o) {
        if (o instanceof AbbreviatedObjectId) {
            final AbbreviatedObjectId b = (AbbreviatedObjectId) o;
            return nibbles == b.nibbles && w1 == b.w1 && w2 == b.w2 && w3 == b.w3 && w4 == b.w4 && w5 == b.w5;
        }
        return false;
    }

    /**
     * Get string form of the abbreviation, in lower case hexadecimal.
     *
     * @return string form of the abbreviation, in lower case hexadecimal.
     */
    public final String name() {
        final char[] b = new char[Constants.OBJECT_ID_STRING_LENGTH];

        AnyObjectId.formatHexChar(b, 0, w1);
        if (nibbles <= 8)
            return new String(b, 0, nibbles);

        AnyObjectId.formatHexChar(b, 8, w2);
        if (nibbles <= 16)
            return new String(b, 0, nibbles);

        AnyObjectId.formatHexChar(b, 16, w3);
        if (nibbles <= 24)
            return new String(b, 0, nibbles);

        AnyObjectId.formatHexChar(b, 24, w4);
        if (nibbles <= 32)
            return new String(b, 0, nibbles);

        AnyObjectId.formatHexChar(b, 32, w5);
        return new String(b, 0, nibbles);
    }

    /** {@inheritDoc} */
    @SuppressWarnings("nls")
    @Override
    public String toString() {
        return "AbbreviatedObjectId[" + name() + "]"; //$NON-NLS-1$
    }
}