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
;
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
) {
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
);
69 return writeBlob(f
.length(), is
);
75 public ObjectId
writeBlob(final long len
, final InputStream is
)
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();
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
);
95 o
.write(e
.getNameUTF8());
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
)
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
);
120 c
.getTreeId().copyTo(os
);
123 ObjectId
[] ps
= c
.getParentIds();
124 for (int i
=0; i
<ps
.length
; ++i
) {
133 w
.write(c
.getAuthor().toExternalString());
137 os
.write(hcommitter
);
139 w
.write(c
.getCommitter().toExternalString());
143 if (!encoding
.equals("UTF-8")) {
146 os
.write(Constants
.encodeASCII(encoding
));
151 w
.write(c
.getMessage());
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
);
167 c
.getObjId().copyTo(w
);
171 w
.write(c
.getType());
179 w
.write(c
.getAuthor().toExternalString());
183 w
.write(c
.getMessage());
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
)
195 return writeObject(Constants
.OBJ_COMMIT
, Constants
.TYPE_COMMIT
, len
, is
);
198 public ObjectId
writeTag(final long len
, final InputStream is
)
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
{
206 final DeflaterOutputStream deflateStream
;
207 final FileOutputStream fileStream
;
210 t
= File
.createTempFile("noz", null, r
.getObjectsDirectory());
211 fileStream
= new FileOutputStream(t
);
212 if (!legacyHeaders
) {
214 int c
= ((typeCode
& 7) << 4) | (int) (sz
& 0xf);
217 fileStream
.write(c
| 0x80);
218 c
= (int) (sz
& 0x7f);
226 deflateStream
= new DeflaterOutputStream(fileStream
, def
);
232 header
= Constants
.encodeASCII(type
);
235 deflateStream
.write(header
);
237 md
.update((byte) ' ');
239 deflateStream
.write((byte) ' ');
241 header
= Constants
.encodeASCII(len
);
244 deflateStream
.write(header
);
248 deflateStream
.write((byte) 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
);
258 throw new IOException("Input did not match supplied length. "
259 + len
+ " bytes are missing.");
261 deflateStream
.close();
263 id
= new ObjectId(md
.digest());
267 deflateStream
.close();
274 if (r
.hasObject(id
)) {
275 // Object is already in the repository so remove
276 // the temporary file.
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
295 throw new ObjectWritingException("Unable to"
296 + " create new object: " + o
);