Use the Git sort order.
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / ObjectWriter.java
blobf9f2bde5bcaa918c33014e5447fbf19ac1a39ff8
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.Iterator;
29 import java.util.zip.Deflater;
30 import java.util.zip.DeflaterOutputStream;
32 import org.spearce.jgit.errors.ObjectWritingException;
34 public class ObjectWriter {
35 private static final byte[] htree = Constants.encodeASCII("tree");
37 private static final byte[] hparent = Constants.encodeASCII("parent");
39 private static final byte[] hauthor = Constants.encodeASCII("author");
41 private static final byte[] hcommitter = Constants.encodeASCII("committer");
43 private static final byte[] hencoding = Constants.encodeASCII("encoding");
45 private final Repository r;
47 private final byte[] buf;
49 private final MessageDigest md;
51 private final Deflater def;
53 private final boolean legacyHeaders;
55 public ObjectWriter(final Repository d) {
56 r = d;
57 buf = new byte[8192];
58 md = Constants.newMessageDigest();
59 def = new Deflater(r.getConfig().getCore().getCompression());
60 legacyHeaders = r.getConfig().getCore().useLegacyHeaders();
63 public ObjectId writeBlob(final byte[] b) throws IOException {
64 return writeBlob(b.length, new ByteArrayInputStream(b));
67 public ObjectId writeBlob(final File f) throws IOException {
68 final FileInputStream is = new FileInputStream(f);
69 try {
70 return writeBlob(f.length(), is);
71 } finally {
72 is.close();
76 public ObjectId writeBlob(final long len, final InputStream is)
77 throws IOException {
78 return writeObject(Constants.OBJ_BLOB, Constants.TYPE_BLOB, len, is);
81 public ObjectId writeTree(final Tree t) throws IOException {
82 final ByteArrayOutputStream o = new ByteArrayOutputStream();
83 final TreeEntry[] items = t.members();
84 for (int k = 0; k < items.length; k++) {
85 final TreeEntry e = items[k];
86 final ObjectId id = e.getId();
88 if (id == null)
89 throw new ObjectWritingException("Object at path \""
90 + e.getFullName() + "\" does not have an id assigned."
91 + " All object ids must be assigned prior"
92 + " to writing a tree.");
94 e.getMode().copyTo(o);
95 o.write(' ');
96 o.write(e.getNameUTF8());
97 o.write(0);
98 o.write(id.getBytes());
100 return writeTree(o.toByteArray());
103 public ObjectId writeTree(final byte[] b) throws IOException {
104 return writeTree(b.length, new ByteArrayInputStream(b));
107 public ObjectId writeTree(final long len, final InputStream is)
108 throws IOException {
109 return writeObject(Constants.OBJ_TREE, Constants.TYPE_TREE, len, is);
112 public ObjectId writeCommit(final Commit c) throws IOException {
113 final ByteArrayOutputStream os = new ByteArrayOutputStream();
114 String encoding = c.getEncoding();
115 if (encoding == null)
116 encoding = Constants.CHARACTER_ENCODING;
117 final OutputStreamWriter w = new OutputStreamWriter(os, encoding);
119 os.write(htree);
120 os.write(' ');
121 c.getTreeId().copyTo(os);
122 os.write('\n');
124 final Iterator i = c.getParentIds().iterator();
125 while (i.hasNext()) {
126 os.write(hparent);
127 os.write(' ');
128 ((ObjectId) i.next()).copyTo(os);
129 os.write('\n');
132 os.write(hauthor);
133 os.write(' ');
134 w.write(c.getAuthor().toExternalString());
135 w.flush();
136 os.write('\n');
138 os.write(hcommitter);
139 os.write(' ');
140 w.write(c.getCommitter().toExternalString());
141 w.flush();
142 os.write('\n');
144 if (!encoding.equals("UTF-8")) {
145 os.write(hencoding);
146 os.write(' ');
147 os.write(Constants.encodeASCII(encoding));
148 os.write('\n');
151 os.write('\n');
152 w.write(c.getMessage());
153 w.flush();
155 return writeCommit(os.toByteArray());
158 public ObjectId writeTag(final byte[] b) throws IOException {
159 return writeTag(b.length, new ByteArrayInputStream(b));
162 public ObjectId writeTag(final Tag c) throws IOException {
163 final ByteArrayOutputStream os = new ByteArrayOutputStream();
164 final OutputStreamWriter w = new OutputStreamWriter(os,
165 Constants.CHARACTER_ENCODING);
167 w.write("object ");
168 c.getObjId().copyTo(w);
169 w.write('\n');
171 w.write("type ");
172 w.write(c.getType());
173 w.write("\n");
175 w.write("tag ");
176 w.write(c.getTag());
177 w.write("\n");
179 w.write("tagger ");
180 w.write(c.getAuthor().toExternalString());
181 w.write('\n');
183 w.write('\n');
184 w.write(c.getMessage());
185 w.close();
187 return writeTag(os.toByteArray());
190 public ObjectId writeCommit(final byte[] b) throws IOException {
191 return writeCommit(b.length, new ByteArrayInputStream(b));
194 public ObjectId writeCommit(final long len, final InputStream is)
195 throws IOException {
196 return writeObject(Constants.OBJ_COMMIT, Constants.TYPE_COMMIT, len, is);
199 public ObjectId writeTag(final long len, final InputStream is)
200 throws IOException {
201 return writeObject(Constants.OBJ_TAG, Constants.TYPE_TAG, len, is);
204 public ObjectId writeObject(final int typeCode, final String type,
205 long len, final InputStream is) throws IOException {
206 final File t;
207 final DeflaterOutputStream deflateStream;
208 final FileOutputStream fileStream;
209 ObjectId id = null;
211 t = File.createTempFile("noz", null, r.getObjectsDirectory());
212 fileStream = new FileOutputStream(t);
213 if (!legacyHeaders) {
214 long sz = len;
215 int c = ((typeCode & 7) << 4) | (int) (sz & 0xf);
216 sz >>= 4;
217 while (sz > 0) {
218 fileStream.write(c | 0x80);
219 c = (int) (sz & 0x7f);
220 sz >>= 7;
222 fileStream.write(c);
225 md.reset();
226 def.reset();
227 deflateStream = new DeflaterOutputStream(fileStream, def);
229 try {
230 byte[] header;
231 int r;
233 header = Constants.encodeASCII(type);
234 md.update(header);
235 if (legacyHeaders)
236 deflateStream.write(header);
238 md.update((byte) ' ');
239 if (legacyHeaders)
240 deflateStream.write((byte) ' ');
242 header = Constants.encodeASCII(len);
243 md.update(header);
244 if (legacyHeaders)
245 deflateStream.write(header);
247 md.update((byte) 0);
248 if (legacyHeaders)
249 deflateStream.write((byte) 0);
251 while (len > 0
252 && (r = is.read(buf, 0, (int) Math.min(len, buf.length))) > 0) {
253 md.update(buf, 0, r);
254 deflateStream.write(buf, 0, r);
255 len -= r;
258 if (len != 0)
259 throw new IOException("Input did not match supplied length. "
260 + len + " bytes are missing.");
262 deflateStream.close();
263 t.setReadOnly();
264 id = new ObjectId(md.digest());
265 } finally {
266 if (id == null) {
267 try {
268 deflateStream.close();
269 } finally {
270 t.delete();
275 if (r.hasObject(id)) {
276 // Object is already in the repository so remove
277 // the temporary file.
279 t.delete();
280 } else {
281 final File o = r.toFile(id);
282 if (!t.renameTo(o)) {
283 // Maybe the directory doesn't exist yet as the object
284 // directories are always lazily created. Note that we
285 // try the rename first as the directory likely does exist.
287 o.getParentFile().mkdir();
288 if (!t.renameTo(o)) {
289 if (!r.hasObject(id)) {
290 // The object failed to be renamed into its proper
291 // location and it doesn't exist in the repository
292 // either. We really don't know what went wrong, so
293 // fail.
295 t.delete();
296 throw new ObjectWritingException("Unable to"
297 + " create new object: " + o);
303 return id;