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
.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
) {
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
);
70 return writeBlob(f
.length(), is
);
76 public ObjectId
writeBlob(final long len
, final InputStream is
)
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();
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
);
96 o
.write(e
.getNameUTF8());
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
)
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
);
121 c
.getTreeId().copyTo(os
);
124 final Iterator i
= c
.getParentIds().iterator();
125 while (i
.hasNext()) {
128 ((ObjectId
) i
.next()).copyTo(os
);
134 w
.write(c
.getAuthor().toExternalString());
138 os
.write(hcommitter
);
140 w
.write(c
.getCommitter().toExternalString());
144 if (!encoding
.equals("UTF-8")) {
147 os
.write(Constants
.encodeASCII(encoding
));
152 w
.write(c
.getMessage());
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
);
168 c
.getObjId().copyTo(w
);
172 w
.write(c
.getType());
180 w
.write(c
.getAuthor().toExternalString());
184 w
.write(c
.getMessage());
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
)
196 return writeObject(Constants
.OBJ_COMMIT
, Constants
.TYPE_COMMIT
, len
, is
);
199 public ObjectId
writeTag(final long len
, final InputStream is
)
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
{
207 final DeflaterOutputStream deflateStream
;
208 final FileOutputStream fileStream
;
211 t
= File
.createTempFile("noz", null, r
.getObjectsDirectory());
212 fileStream
= new FileOutputStream(t
);
213 if (!legacyHeaders
) {
215 int c
= ((typeCode
& 7) << 4) | (int) (sz
& 0xf);
218 fileStream
.write(c
| 0x80);
219 c
= (int) (sz
& 0x7f);
227 deflateStream
= new DeflaterOutputStream(fileStream
, def
);
233 header
= Constants
.encodeASCII(type
);
236 deflateStream
.write(header
);
238 md
.update((byte) ' ');
240 deflateStream
.write((byte) ' ');
242 header
= Constants
.encodeASCII(len
);
245 deflateStream
.write(header
);
249 deflateStream
.write((byte) 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
);
259 throw new IOException("Input did not match supplied length. "
260 + len
+ " bytes are missing.");
262 deflateStream
.close();
264 id
= new ObjectId(md
.digest());
268 deflateStream
.close();
275 if (r
.hasObject(id
)) {
276 // Object is already in the repository so remove
277 // the temporary file.
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
296 throw new ObjectWritingException("Unable to"
297 + " create new object: " + o
);