Use ObjectId[] instead of List for parents
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / ObjectWriter.java
blobb947a8091c2a2cae63f4a9dda0c3b9917e1cb123
1 /*
2 * Copyright (C) 2006 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStreamWriter;
27 import java.security.MessageDigest;
28 import java.util.zip.Deflater;
29 import java.util.zip.DeflaterOutputStream;
31 import org.spearce.jgit.errors.ObjectWritingException;
33 public class ObjectWriter {
34 private static final byte[] htree = Constants.encodeASCII("tree");
36 private static final byte[] hparent = Constants.encodeASCII("parent");
38 private static final byte[] hauthor = Constants.encodeASCII("author");
40 private static final byte[] hcommitter = Constants.encodeASCII("committer");
42 private static final byte[] hencoding = Constants.encodeASCII("encoding");
44 private final Repository r;
46 private final byte[] buf;
48 private final MessageDigest md;
50 private final Deflater def;
52 private final boolean legacyHeaders;
54 public ObjectWriter(final Repository d) {
55 r = d;
56 buf = new byte[8192];
57 md = Constants.newMessageDigest();
58 def = new Deflater(r.getConfig().getCore().getCompression());
59 legacyHeaders = r.getConfig().getCore().useLegacyHeaders();
62 public ObjectId writeBlob(final byte[] b) throws IOException {
63 return writeBlob(b.length, new ByteArrayInputStream(b));
66 public ObjectId writeBlob(final File f) throws IOException {
67 final FileInputStream is = new FileInputStream(f);
68 try {
69 return writeBlob(f.length(), is);
70 } finally {
71 is.close();
75 public ObjectId writeBlob(final long len, final InputStream is)
76 throws IOException {
77 return writeObject(Constants.OBJ_BLOB, Constants.TYPE_BLOB, len, is);
80 public ObjectId writeTree(final Tree t) throws IOException {
81 final ByteArrayOutputStream o = new ByteArrayOutputStream();
82 final TreeEntry[] items = t.members();
83 for (int k = 0; k < items.length; k++) {
84 final TreeEntry e = items[k];
85 final ObjectId id = e.getId();
87 if (id == null)
88 throw new ObjectWritingException("Object at path \""
89 + e.getFullName() + "\" does not have an id assigned."
90 + " All object ids must be assigned prior"
91 + " to writing a tree.");
93 e.getMode().copyTo(o);
94 o.write(' ');
95 o.write(e.getNameUTF8());
96 o.write(0);
97 o.write(id.getBytes());
99 return writeTree(o.toByteArray());
102 public ObjectId writeTree(final byte[] b) throws IOException {
103 return writeTree(b.length, new ByteArrayInputStream(b));
106 public ObjectId writeTree(final long len, final InputStream is)
107 throws IOException {
108 return writeObject(Constants.OBJ_TREE, Constants.TYPE_TREE, len, is);
111 public ObjectId writeCommit(final Commit c) throws IOException {
112 final ByteArrayOutputStream os = new ByteArrayOutputStream();
113 String encoding = c.getEncoding();
114 if (encoding == null)
115 encoding = Constants.CHARACTER_ENCODING;
116 final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
118 os.write(htree);
119 os.write(' ');
120 c.getTreeId().copyTo(os);
121 os.write('\n');
123 ObjectId[] ps = c.getParentIds();
124 for (int i=0; i<ps.length; ++i) {
125 os.write(hparent);
126 os.write(' ');
127 ps[i].copyTo(os);
128 os.write('\n');
131 os.write(hauthor);
132 os.write(' ');
133 w.write(c.getAuthor().toExternalString());
134 w.flush();
135 os.write('\n');
137 os.write(hcommitter);
138 os.write(' ');
139 w.write(c.getCommitter().toExternalString());
140 w.flush();
141 os.write('\n');
143 if (!encoding.equals("UTF-8")) {
144 os.write(hencoding);
145 os.write(' ');
146 os.write(Constants.encodeASCII(encoding));
147 os.write('\n');
150 os.write('\n');
151 w.write(c.getMessage());
152 w.flush();
154 return writeCommit(os.toByteArray());
157 public ObjectId writeTag(final byte[] b) throws IOException {
158 return writeTag(b.length, new ByteArrayInputStream(b));
161 public ObjectId writeTag(final Tag c) throws IOException {
162 final ByteArrayOutputStream os = new ByteArrayOutputStream();
163 final OutputStreamWriter w = new OutputStreamWriter(os,
164 Constants.CHARACTER_ENCODING);
166 w.write("object ");
167 c.getObjId().copyTo(w);
168 w.write('\n');
170 w.write("type ");
171 w.write(c.getType());
172 w.write("\n");
174 w.write("tag ");
175 w.write(c.getTag());
176 w.write("\n");
178 w.write("tagger ");
179 w.write(c.getAuthor().toExternalString());
180 w.write('\n');
182 w.write('\n');
183 w.write(c.getMessage());
184 w.close();
186 return writeTag(os.toByteArray());
189 public ObjectId writeCommit(final byte[] b) throws IOException {
190 return writeCommit(b.length, new ByteArrayInputStream(b));
193 public ObjectId writeCommit(final long len, final InputStream is)
194 throws IOException {
195 return writeObject(Constants.OBJ_COMMIT, Constants.TYPE_COMMIT, len, is);
198 public ObjectId writeTag(final long len, final InputStream is)
199 throws IOException {
200 return writeObject(Constants.OBJ_TAG, Constants.TYPE_TAG, len, is);
203 public ObjectId writeObject(final int typeCode, final String type,
204 long len, final InputStream is) throws IOException {
205 final File t;
206 final DeflaterOutputStream deflateStream;
207 final FileOutputStream fileStream;
208 ObjectId id = null;
210 t = File.createTempFile("noz", null, r.getObjectsDirectory());
211 fileStream = new FileOutputStream(t);
212 if (!legacyHeaders) {
213 long sz = len;
214 int c = ((typeCode & 7) << 4) | (int) (sz & 0xf);
215 sz >>= 4;
216 while (sz > 0) {
217 fileStream.write(c | 0x80);
218 c = (int) (sz & 0x7f);
219 sz >>= 7;
221 fileStream.write(c);
224 md.reset();
225 def.reset();
226 deflateStream = new DeflaterOutputStream(fileStream, def);
228 try {
229 byte[] header;
230 int r;
232 header = Constants.encodeASCII(type);
233 md.update(header);
234 if (legacyHeaders)
235 deflateStream.write(header);
237 md.update((byte) ' ');
238 if (legacyHeaders)
239 deflateStream.write((byte) ' ');
241 header = Constants.encodeASCII(len);
242 md.update(header);
243 if (legacyHeaders)
244 deflateStream.write(header);
246 md.update((byte) 0);
247 if (legacyHeaders)
248 deflateStream.write((byte) 0);
250 while (len > 0
251 && (r = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
252 md.update(buf, 0, r);
253 deflateStream.write(buf, 0, r);
254 len -= r;
257 if (len != 0)
258 throw new IOException("Input did not match supplied length. "
259 + len + " bytes are missing.");
261 deflateStream.close();
262 t.setReadOnly();
263 id = new ObjectId(md.digest());
264 } finally {
265 if (id == null) {
266 try {
267 deflateStream.close();
268 } finally {
269 t.delete();
274 if (r.hasObject(id)) {
275 // Object is already in the repository so remove
276 // the temporary file.
278 t.delete();
279 } else {
280 final File o = r.toFile(id);
281 if (!t.renameTo(o)) {
282 // Maybe the directory doesn't exist yet as the object
283 // directories are always lazily created. Note that we
284 // try the rename first as the directory likely does exist.
286 o.getParentFile().mkdir();
287 if (!t.renameTo(o)) {
288 if (!r.hasObject(id)) {
289 // The object failed to be renamed into its proper
290 // location and it doesn't exist in the repository
291 // either. We really don't know what went wrong, so
292 // fail.
294 t.delete();
295 throw new ObjectWritingException("Unable to"
296 + " create new object: " + o);
302 return id;