| /* |
| * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> |
| * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> |
| * Copyright (C) 2009, Google Inc. |
| * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> |
| * 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.ByteArrayInputStream; |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.MessageDigest; |
| |
| /** |
| * Inserts objects into an existing {@code ObjectDatabase}. |
| * <p> |
| * An inserter is not thread-safe. Individual threads should each obtain their |
| * own unique inserter instance, or must arrange for locking at a higher level |
| * to ensure the inserter is in use by no more than one thread at a time. |
| * <p> |
| * Objects written by an inserter may not be immediately visible for reading |
| * after the insert method completes. Callers must invoke either |
| * {@link #release()} or {@link #flush()} prior to updating references or |
| * otherwise making the returned ObjectIds visible to other code. |
| */ |
| public abstract class ObjectInserter { |
| /** An inserter that can be used for formatting and id generation only. */ |
| public static class Formatter extends ObjectInserter { |
| @Override |
| public ObjectId insert(int objectType, long length, InputStream in) |
| throws IOException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void flush() throws IOException { |
| // Do nothing. |
| } |
| |
| @Override |
| public void release() { |
| // Do nothing. |
| } |
| } |
| |
| /** Digest to compute the name of an object. */ |
| private final MessageDigest digest; |
| |
| /** Temporary working buffer for streaming data through. */ |
| private byte[] tempBuffer; |
| |
| /** Create a new inserter for a database. */ |
| protected ObjectInserter() { |
| digest = Constants.newMessageDigest(); |
| } |
| |
| /** @return a temporary byte array for use by the caller. */ |
| protected byte[] buffer() { |
| if (tempBuffer == null) |
| tempBuffer = new byte[8192]; |
| return tempBuffer; |
| } |
| |
| /** @return digest to help compute an ObjectId */ |
| protected MessageDigest digest() { |
| digest.reset(); |
| return digest; |
| } |
| |
| /** |
| * Compute the name of an object, without inserting it. |
| * |
| * @param type |
| * type code of the object to store. |
| * @param data |
| * complete content of the object. |
| * @return the name of the object. |
| */ |
| public ObjectId idFor(int type, byte[] data) { |
| return idFor(type, data, 0, data.length); |
| } |
| |
| /** |
| * Compute the name of an object, without inserting it. |
| * |
| * @param type |
| * type code of the object to store. |
| * @param data |
| * complete content of the object. |
| * @param off |
| * first position within {@code data}. |
| * @param len |
| * number of bytes to copy from {@code data}. |
| * @return the name of the object. |
| */ |
| public ObjectId idFor(int type, byte[] data, int off, int len) { |
| MessageDigest md = digest(); |
| md.update(Constants.encodedTypeString(type)); |
| md.update((byte) ' '); |
| md.update(Constants.encodeASCII(len)); |
| md.update((byte) 0); |
| md.update(data, off, len); |
| return ObjectId.fromRaw(md.digest()); |
| } |
| |
| /** |
| * Compute the name of an object, without inserting it. |
| * |
| * @param objectType |
| * type code of the object to store. |
| * @param length |
| * number of bytes to scan from {@code in}. |
| * @param in |
| * stream providing the object content. The caller is responsible |
| * for closing the stream. |
| * @return the name of the object. |
| * @throws IOException |
| * the source stream could not be read. |
| */ |
| public ObjectId idFor(int objectType, long length, InputStream in) |
| throws IOException { |
| MessageDigest md = digest(); |
| md.update(Constants.encodedTypeString(objectType)); |
| md.update((byte) ' '); |
| md.update(Constants.encodeASCII(length)); |
| md.update((byte) 0); |
| byte[] buf = buffer(); |
| while (length > 0) { |
| int n = in.read(buf, 0, (int) Math.min(length, buf.length)); |
| if (n < 0) |
| throw new EOFException("Unexpected end of input"); |
| md.update(buf, 0, n); |
| length -= n; |
| } |
| return ObjectId.fromRaw(md.digest()); |
| } |
| |
| /** |
| * Insert a single commit into the store, returning its unique name. |
| * |
| * As a side effect, {@link CommitBuilder#getCommitId()} will also be |
| * populated with the returned ObjectId. |
| * |
| * @param builder |
| * the builder containing the proposed commit's data. |
| * @return the name of the commit object. |
| * @throws IOException |
| * the object could not be stored. |
| */ |
| public final ObjectId insert(CommitBuilder builder) throws IOException { |
| return insert(Constants.OBJ_COMMIT, builder.format(this)); |
| } |
| |
| /** |
| * Insert a single annotated tag into the store, returning its unique name. |
| * |
| * As a side effect, {@link TagBuilder#getTagId()} will also be populated |
| * with the returned ObjectId. |
| * |
| * @param builder |
| * the builder containing the proposed tag's data. |
| * @return the name of the tag object. |
| * @throws IOException |
| * the object could not be stored. |
| */ |
| public final ObjectId insert(TagBuilder builder) throws IOException { |
| return insert(Constants.OBJ_TAG, builder.format(this)); |
| } |
| |
| /** |
| * Insert a single object into the store, returning its unique name. |
| * |
| * @param type |
| * type code of the object to store. |
| * @param data |
| * complete content of the object. |
| * @return the name of the object. |
| * @throws IOException |
| * the object could not be stored. |
| */ |
| public ObjectId insert(final int type, final byte[] data) |
| throws IOException { |
| return insert(type, data, 0, data.length); |
| } |
| |
| /** |
| * Insert a single object into the store, returning its unique name. |
| * |
| * @param type |
| * type code of the object to store. |
| * @param data |
| * complete content of the object. |
| * @param off |
| * first position within {@code data}. |
| * @param len |
| * number of bytes to copy from {@code data}. |
| * @return the name of the object. |
| * @throws IOException |
| * the object could not be stored. |
| */ |
| public ObjectId insert(int type, byte[] data, int off, int len) |
| throws IOException { |
| return insert(type, len, new ByteArrayInputStream(data, off, len)); |
| } |
| |
| /** |
| * Insert a single object into the store, returning its unique name. |
| * |
| * @param objectType |
| * type code of the object to store. |
| * @param length |
| * number of bytes to copy from {@code in}. |
| * @param in |
| * stream providing the object content. The caller is responsible |
| * for closing the stream. |
| * @return the name of the object. |
| * @throws IOException |
| * the object could not be stored, or the source stream could |
| * not be read. |
| */ |
| public abstract ObjectId insert(int objectType, long length, InputStream in) |
| throws IOException; |
| |
| /** |
| * Make all inserted objects visible. |
| * <p> |
| * The flush may take some period of time to make the objects available to |
| * other threads. |
| * |
| * @throws IOException |
| * the flush could not be completed; objects inserted thus far |
| * are in an indeterminate state. |
| */ |
| public abstract void flush() throws IOException; |
| |
| /** |
| * Release any resources used by this inserter. |
| * <p> |
| * An inserter that has been released can be used again, but may need to be |
| * released after the subsequent usage. |
| */ |
| public abstract void release(); |
| } |