Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java412
1 files changed, 412 insertions, 0 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
new file mode 100644
index 0000000000..60e85eb57f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
+ * 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.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.security.MessageDigest;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.eclipse.jgit.errors.ObjectWritingException;
+
+/**
+ * A class for writing loose objects.
+ */
+public class ObjectWriter {
+ private static final byte[] htree = Constants.encodeASCII("tree");
+
+ private static final byte[] hparent = Constants.encodeASCII("parent");
+
+ private static final byte[] hauthor = Constants.encodeASCII("author");
+
+ private static final byte[] hcommitter = Constants.encodeASCII("committer");
+
+ private static final byte[] hencoding = Constants.encodeASCII("encoding");
+
+ private final Repository r;
+
+ private final byte[] buf;
+
+ private final MessageDigest md;
+
+ private final Deflater def;
+
+ /**
+ * Construct an Object writer for the specified repository
+ * @param d
+ */
+ public ObjectWriter(final Repository d) {
+ r = d;
+ buf = new byte[8192];
+ md = Constants.newMessageDigest();
+ def = new Deflater(r.getConfig().getCore().getCompression());
+ }
+
+ /**
+ * Write a blob with the specified data
+ *
+ * @param b bytes of the blob
+ * @return SHA-1 of the blob
+ * @throws IOException
+ */
+ public ObjectId writeBlob(final byte[] b) throws IOException {
+ return writeBlob(b.length, new ByteArrayInputStream(b));
+ }
+
+ /**
+ * Write a blob with the data in the specified file
+ *
+ * @param f
+ * a file containing blob data
+ * @return SHA-1 of the blob
+ * @throws IOException
+ */
+ public ObjectId writeBlob(final File f) throws IOException {
+ final FileInputStream is = new FileInputStream(f);
+ try {
+ return writeBlob(f.length(), is);
+ } finally {
+ is.close();
+ }
+ }
+
+ /**
+ * Write a blob with data from a stream
+ *
+ * @param len
+ * number of bytes to consume from the stream
+ * @param is
+ * stream with blob data
+ * @return SHA-1 of the blob
+ * @throws IOException
+ */
+ public ObjectId writeBlob(final long len, final InputStream is)
+ throws IOException {
+ return writeObject(Constants.OBJ_BLOB, len, is, true);
+ }
+
+ /**
+ * Write a Tree to the object database.
+ *
+ * @param t
+ * Tree
+ * @return SHA-1 of the tree
+ * @throws IOException
+ */
+ public ObjectId writeTree(final Tree t) throws IOException {
+ final ByteArrayOutputStream o = new ByteArrayOutputStream();
+ final TreeEntry[] items = t.members();
+ for (int k = 0; k < items.length; k++) {
+ final TreeEntry e = items[k];
+ final ObjectId id = e.getId();
+
+ if (id == null)
+ throw new ObjectWritingException("Object at path \""
+ + e.getFullName() + "\" does not have an id assigned."
+ + " All object ids must be assigned prior"
+ + " to writing a tree.");
+
+ e.getMode().copyTo(o);
+ o.write(' ');
+ o.write(e.getNameUTF8());
+ o.write(0);
+ id.copyRawTo(o);
+ }
+ return writeCanonicalTree(o.toByteArray());
+ }
+
+ /**
+ * Write a canonical tree to the object database.
+ *
+ * @param b
+ * the canonical encoding of the tree object.
+ * @return SHA-1 of the tree
+ * @throws IOException
+ */
+ public ObjectId writeCanonicalTree(final byte[] b) throws IOException {
+ return writeTree(b.length, new ByteArrayInputStream(b));
+ }
+
+ private ObjectId writeTree(final long len, final InputStream is)
+ throws IOException {
+ return writeObject(Constants.OBJ_TREE, len, is, true);
+ }
+
+ /**
+ * Write a Commit to the object database
+ *
+ * @param c
+ * Commit to store
+ * @return SHA-1 of the commit
+ * @throws IOException
+ */
+ public ObjectId writeCommit(final Commit c) throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ String encoding = c.getEncoding();
+ if (encoding == null)
+ encoding = Constants.CHARACTER_ENCODING;
+ final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
+
+ os.write(htree);
+ os.write(' ');
+ c.getTreeId().copyTo(os);
+ os.write('\n');
+
+ ObjectId[] ps = c.getParentIds();
+ for (int i=0; i<ps.length; ++i) {
+ os.write(hparent);
+ os.write(' ');
+ ps[i].copyTo(os);
+ os.write('\n');
+ }
+
+ os.write(hauthor);
+ os.write(' ');
+ w.write(c.getAuthor().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ os.write(hcommitter);
+ os.write(' ');
+ w.write(c.getCommitter().toExternalString());
+ w.flush();
+ os.write('\n');
+
+ if (!encoding.equals(Constants.CHARACTER_ENCODING)) {
+ os.write(hencoding);
+ os.write(' ');
+ os.write(Constants.encodeASCII(encoding));
+ os.write('\n');
+ }
+
+ os.write('\n');
+ w.write(c.getMessage());
+ w.flush();
+
+ return writeCommit(os.toByteArray());
+ }
+
+ private ObjectId writeTag(final byte[] b) throws IOException {
+ return writeTag(b.length, new ByteArrayInputStream(b));
+ }
+
+ /**
+ * Write an annotated Tag to the object database
+ *
+ * @param c
+ * Tag
+ * @return SHA-1 of the tag
+ * @throws IOException
+ */
+ public ObjectId writeTag(final Tag c) throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ final OutputStreamWriter w = new OutputStreamWriter(os,
+ Constants.CHARSET);
+
+ w.write("object ");
+ c.getObjId().copyTo(w);
+ w.write('\n');
+
+ w.write("type ");
+ w.write(c.getType());
+ w.write("\n");
+
+ w.write("tag ");
+ w.write(c.getTag());
+ w.write("\n");
+
+ w.write("tagger ");
+ w.write(c.getAuthor().toExternalString());
+ w.write('\n');
+
+ w.write('\n');
+ w.write(c.getMessage());
+ w.close();
+
+ return writeTag(os.toByteArray());
+ }
+
+ private ObjectId writeCommit(final byte[] b) throws IOException {
+ return writeCommit(b.length, new ByteArrayInputStream(b));
+ }
+
+ private ObjectId writeCommit(final long len, final InputStream is)
+ throws IOException {
+ return writeObject(Constants.OBJ_COMMIT, len, is, true);
+ }
+
+ private ObjectId writeTag(final long len, final InputStream is)
+ throws IOException {
+ return writeObject(Constants.OBJ_TAG, len, is, true);
+ }
+
+ /**
+ * Compute the SHA-1 of a blob without creating an object. This is for
+ * figuring out if we already have a blob or not.
+ *
+ * @param len number of bytes to consume
+ * @param is stream for read blob data from
+ * @return SHA-1 of a looked for blob
+ * @throws IOException
+ */
+ public ObjectId computeBlobSha1(final long len, final InputStream is)
+ throws IOException {
+ return writeObject(Constants.OBJ_BLOB, len, is, false);
+ }
+
+ ObjectId writeObject(final int type, long len, final InputStream is,
+ boolean store) throws IOException {
+ final File t;
+ final DeflaterOutputStream deflateStream;
+ final FileOutputStream fileStream;
+ ObjectId id = null;
+
+ if (store) {
+ t = File.createTempFile("noz", null, r.getObjectsDirectory());
+ fileStream = new FileOutputStream(t);
+ } else {
+ t = null;
+ fileStream = null;
+ }
+
+ md.reset();
+ if (store) {
+ def.reset();
+ deflateStream = new DeflaterOutputStream(fileStream, def);
+ } else
+ deflateStream = null;
+
+ try {
+ byte[] header;
+ int n;
+
+ header = Constants.encodedTypeString(type);
+ md.update(header);
+ if (deflateStream != null)
+ deflateStream.write(header);
+
+ md.update((byte) ' ');
+ if (deflateStream != null)
+ deflateStream.write((byte) ' ');
+
+ header = Constants.encodeASCII(len);
+ md.update(header);
+ if (deflateStream != null)
+ deflateStream.write(header);
+
+ md.update((byte) 0);
+ if (deflateStream != null)
+ deflateStream.write((byte) 0);
+
+ while (len > 0
+ && (n = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
+ md.update(buf, 0, n);
+ if (deflateStream != null)
+ deflateStream.write(buf, 0, n);
+ len -= n;
+ }
+
+ if (len != 0)
+ throw new IOException("Input did not match supplied length. "
+ + len + " bytes are missing.");
+
+ if (deflateStream != null ) {
+ deflateStream.close();
+ if (t != null)
+ t.setReadOnly();
+ }
+
+ id = ObjectId.fromRaw(md.digest());
+ } finally {
+ if (id == null && deflateStream != null) {
+ try {
+ deflateStream.close();
+ } finally {
+ t.delete();
+ }
+ }
+ }
+
+ if (t == null)
+ return id;
+
+ if (r.hasObject(id)) {
+ // Object is already in the repository so remove
+ // the temporary file.
+ //
+ t.delete();
+ } else {
+ final File o = r.toFile(id);
+ if (!t.renameTo(o)) {
+ // Maybe the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we
+ // try the rename first as the directory likely does exist.
+ //
+ o.getParentFile().mkdir();
+ if (!t.renameTo(o)) {
+ if (!r.hasObject(id)) {
+ // The object failed to be renamed into its proper
+ // location and it doesn't exist in the repository
+ // either. We really don't know what went wrong, so
+ // fail.
+ //
+ t.delete();
+ throw new ObjectWritingException("Unable to"
+ + " create new object: " + o);
+ }
+ }
+ }
+ }
+
+ return id;
+ }
+}

Back to the top