Fix Repository isValidRefName() for empty names
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / lib / ObjectWriter.java
blob6c2cd4f7669d25a47898e619b928938f8c8c622f
1 /*
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.lib;
41 import java.io.ByteArrayInputStream;
42 import java.io.ByteArrayOutputStream;
43 import java.io.File;
44 import java.io.FileInputStream;
45 import java.io.FileOutputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStreamWriter;
49 import java.security.MessageDigest;
50 import java.util.zip.Deflater;
51 import java.util.zip.DeflaterOutputStream;
53 import org.spearce.jgit.errors.ObjectWritingException;
55 /**
56 * A class for writing loose objects.
58 public class ObjectWriter {
59 private static final byte[] htree = Constants.encodeASCII("tree");
61 private static final byte[] hparent = Constants.encodeASCII("parent");
63 private static final byte[] hauthor = Constants.encodeASCII("author");
65 private static final byte[] hcommitter = Constants.encodeASCII("committer");
67 private static final byte[] hencoding = Constants.encodeASCII("encoding");
69 private final Repository r;
71 private final byte[] buf;
73 private final MessageDigest md;
75 private final Deflater def;
77 /**
78 * Construct an Object writer for the specified repository
79 * @param d
81 public ObjectWriter(final Repository d) {
82 r = d;
83 buf = new byte[8192];
84 md = Constants.newMessageDigest();
85 def = new Deflater(r.getConfig().getCore().getCompression());
88 /**
89 * Write a blob with the specified data
91 * @param b bytes of the blob
92 * @return SHA-1 of the blob
93 * @throws IOException
95 public ObjectId writeBlob(final byte[] b) throws IOException {
96 return writeBlob(b.length, new ByteArrayInputStream(b));
99 /**
100 * Write a blob with the data in the specified file
102 * @param f
103 * a file containing blob data
104 * @return SHA-1 of the blob
105 * @throws IOException
107 public ObjectId writeBlob(final File f) throws IOException {
108 final FileInputStream is = new FileInputStream(f);
109 try {
110 return writeBlob(f.length(), is);
111 } finally {
112 is.close();
117 * Write a blob with data from a stream
119 * @param len
120 * number of bytes to consume from the stream
121 * @param is
122 * stream with blob data
123 * @return SHA-1 of the blob
124 * @throws IOException
126 public ObjectId writeBlob(final long len, final InputStream is)
127 throws IOException {
128 return writeObject(Constants.OBJ_BLOB, len, is, true);
132 * Write a Tree to the object database.
134 * @param t
135 * Tree
136 * @return SHA-1 of the tree
137 * @throws IOException
139 public ObjectId writeTree(final Tree t) throws IOException {
140 final ByteArrayOutputStream o = new ByteArrayOutputStream();
141 final TreeEntry[] items = t.members();
142 for (int k = 0; k < items.length; k++) {
143 final TreeEntry e = items[k];
144 final ObjectId id = e.getId();
146 if (id == null)
147 throw new ObjectWritingException("Object at path \""
148 + e.getFullName() + "\" does not have an id assigned."
149 + " All object ids must be assigned prior"
150 + " to writing a tree.");
152 e.getMode().copyTo(o);
153 o.write(' ');
154 o.write(e.getNameUTF8());
155 o.write(0);
156 id.copyRawTo(o);
158 return writeTree(o.toByteArray());
161 private ObjectId writeTree(final byte[] b) throws IOException {
162 return writeTree(b.length, new ByteArrayInputStream(b));
165 private ObjectId writeTree(final long len, final InputStream is)
166 throws IOException {
167 return writeObject(Constants.OBJ_TREE, len, is, true);
171 * Write a Commit to the object database
173 * @param c
174 * Commit to store
175 * @return SHA-1 of the commit
176 * @throws IOException
178 public ObjectId writeCommit(final Commit c) throws IOException {
179 final ByteArrayOutputStream os = new ByteArrayOutputStream();
180 String encoding = c.getEncoding();
181 if (encoding == null)
182 encoding = Constants.CHARACTER_ENCODING;
183 final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
185 os.write(htree);
186 os.write(' ');
187 c.getTreeId().copyTo(os);
188 os.write('\n');
190 ObjectId[] ps = c.getParentIds();
191 for (int i=0; i<ps.length; ++i) {
192 os.write(hparent);
193 os.write(' ');
194 ps[i].copyTo(os);
195 os.write('\n');
198 os.write(hauthor);
199 os.write(' ');
200 w.write(c.getAuthor().toExternalString());
201 w.flush();
202 os.write('\n');
204 os.write(hcommitter);
205 os.write(' ');
206 w.write(c.getCommitter().toExternalString());
207 w.flush();
208 os.write('\n');
210 if (!encoding.equals("UTF-8")) {
211 os.write(hencoding);
212 os.write(' ');
213 os.write(Constants.encodeASCII(encoding));
214 os.write('\n');
217 os.write('\n');
218 w.write(c.getMessage());
219 w.flush();
221 return writeCommit(os.toByteArray());
224 private ObjectId writeTag(final byte[] b) throws IOException {
225 return writeTag(b.length, new ByteArrayInputStream(b));
229 * Write an annotated Tag to the object database
231 * @param c
232 * Tag
233 * @return SHA-1 of the tag
234 * @throws IOException
236 public ObjectId writeTag(final Tag c) throws IOException {
237 final ByteArrayOutputStream os = new ByteArrayOutputStream();
238 final OutputStreamWriter w = new OutputStreamWriter(os,
239 Constants.CHARSET);
241 w.write("object ");
242 c.getObjId().copyTo(w);
243 w.write('\n');
245 w.write("type ");
246 w.write(c.getType());
247 w.write("\n");
249 w.write("tag ");
250 w.write(c.getTag());
251 w.write("\n");
253 w.write("tagger ");
254 w.write(c.getAuthor().toExternalString());
255 w.write('\n');
257 w.write('\n');
258 w.write(c.getMessage());
259 w.close();
261 return writeTag(os.toByteArray());
264 private ObjectId writeCommit(final byte[] b) throws IOException {
265 return writeCommit(b.length, new ByteArrayInputStream(b));
268 private ObjectId writeCommit(final long len, final InputStream is)
269 throws IOException {
270 return writeObject(Constants.OBJ_COMMIT, len, is, true);
273 private ObjectId writeTag(final long len, final InputStream is)
274 throws IOException {
275 return writeObject(Constants.OBJ_TAG, len, is, true);
279 * Compute the SHA-1 of a blob without creating an object. This is for
280 * figuring out if we already have a blob or not.
282 * @param len number of bytes to consume
283 * @param is stream for read blob data from
284 * @return SHA-1 of a looked for blob
285 * @throws IOException
287 public ObjectId computeBlobSha1(final long len, final InputStream is)
288 throws IOException {
289 return writeObject(Constants.OBJ_BLOB, len, is, false);
292 @SuppressWarnings("null")
293 ObjectId writeObject(final int type, long len, final InputStream is,
294 boolean store) throws IOException {
295 final File t;
296 final DeflaterOutputStream deflateStream;
297 final FileOutputStream fileStream;
298 ObjectId id = null;
300 if (store) {
301 t = File.createTempFile("noz", null, r.getObjectsDirectory());
302 fileStream = new FileOutputStream(t);
303 } else {
304 t = null;
305 fileStream = null;
308 md.reset();
309 if (store) {
310 def.reset();
311 deflateStream = new DeflaterOutputStream(fileStream, def);
312 } else
313 deflateStream = null;
315 try {
316 byte[] header;
317 int n;
319 header = Constants.encodedTypeString(type);
320 md.update(header);
321 if (deflateStream != null)
322 deflateStream.write(header);
324 md.update((byte) ' ');
325 if (deflateStream != null)
326 deflateStream.write((byte) ' ');
328 header = Constants.encodeASCII(len);
329 md.update(header);
330 if (deflateStream != null)
331 deflateStream.write(header);
333 md.update((byte) 0);
334 if (deflateStream != null)
335 deflateStream.write((byte) 0);
337 while (len > 0
338 && (n = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
339 md.update(buf, 0, n);
340 if (deflateStream != null)
341 deflateStream.write(buf, 0, n);
342 len -= n;
345 if (len != 0)
346 throw new IOException("Input did not match supplied length. "
347 + len + " bytes are missing.");
349 if (deflateStream != null ) {
350 deflateStream.close();
351 if (t != null)
352 t.setReadOnly();
355 id = ObjectId.fromRaw(md.digest());
356 } finally {
357 if (id == null && deflateStream != null) {
358 try {
359 deflateStream.close();
360 } finally {
361 t.delete();
366 if (t == null)
367 return id;
369 if (r.hasObject(id)) {
370 // Object is already in the repository so remove
371 // the temporary file.
373 t.delete();
374 } else {
375 final File o = r.toFile(id);
376 if (!t.renameTo(o)) {
377 // Maybe the directory doesn't exist yet as the object
378 // directories are always lazily created. Note that we
379 // try the rename first as the directory likely does exist.
381 o.getParentFile().mkdir();
382 if (!t.renameTo(o)) {
383 if (!r.hasObject(id)) {
384 // The object failed to be renamed into its proper
385 // location and it doesn't exist in the repository
386 // either. We really don't know what went wrong, so
387 // fail.
389 t.delete();
390 throw new ObjectWritingException("Unable to"
391 + " create new object: " + o);
397 return id;