1 /*
2 * Copyright (C) 2010, Google Inc.
3 * and other copyright owners as documented in the project's IP log.
4 *
5 * This program and the accompanying materials are made available
6 * under the terms of the Eclipse Distribution License v1.0 which
7 * accompanies this distribution, is reproduced below, and is
8 * available at http://www.eclipse.org/org/documents/edl-v10.php
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * - Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials provided
22 * with the distribution.
23 *
24 * - Neither the name of the Eclipse Foundation, Inc. nor the
25 * names of its contributors may be used to endorse or promote
26 * products derived from this software without specific prior
27 * written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 package org.eclipse.jgit.lib;
45
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Collection;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.Set;
52
53 import org.eclipse.jgit.annotations.Nullable;
54 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
55 import org.eclipse.jgit.errors.MissingObjectException;
56 import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
57
58 /**
59 * Reads an {@link ObjectDatabase} for a single thread.
60 * <p>
61 * Readers that can support efficient reuse of pack encoded objects should also
62 * implement the companion interface {@link ObjectReuseAsIs}.
63 */
64 public abstract class ObjectReader implements AutoCloseable {
65 /** Type hint indicating the caller doesn't know the type. */
66 public static final int OBJ_ANY = -1;
67
68 /**
69 * Construct a new reader from the same data.
70 * <p>
71 * Applications can use this method to build a new reader from the same data
72 * source, but for an different thread.
73 *
74 * @return a brand new reader, using the same data source.
75 */
76 public abstract ObjectReader newReader();
77
78 /**
79 * Obtain a unique abbreviation (prefix) of an object SHA-1.
80 *
81 * This method uses a reasonable default for the minimum length. Callers who
82 * don't care about the minimum length should prefer this method.
83 *
84 * The returned abbreviation would expand back to the argument ObjectId when
85 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
86 * are added to this repository between calls.
87 *
88 * @param objectId
89 * object identity that needs to be abbreviated.
90 * @return SHA-1 abbreviation.
91 * @throws IOException
92 * the object store cannot be read.
93 */
94 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
95 throws IOException {
96 return abbreviate(objectId, 7);
97 }
98
99 /**
100 * Obtain a unique abbreviation (prefix) of an object SHA-1.
101 *
102 * The returned abbreviation would expand back to the argument ObjectId when
103 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
104 * are added to this repository between calls.
105 *
106 * The default implementation of this method abbreviates the id to the
107 * minimum length, then resolves it to see if there are multiple results.
108 * When multiple results are found, the length is extended by 1 and resolve
109 * is tried again.
110 *
111 * @param objectId
112 * object identity that needs to be abbreviated.
113 * @param len
114 * minimum length of the abbreviated string. Must be in the range
115 * [2, {@value Constants#OBJECT_ID_STRING_LENGTH}].
116 * @return SHA-1 abbreviation. If no matching objects exist in the
117 * repository, the abbreviation will match the minimum length.
118 * @throws IOException
119 * the object store cannot be read.
120 */
121 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
122 throws IOException {
123 if (len == Constants.OBJECT_ID_STRING_LENGTH)
124 return AbbreviatedObjectId.fromObjectId(objectId);
125
126 AbbreviatedObjectId abbrev = objectId.abbreviate(len);
127 Collection<ObjectId> matches = resolve(abbrev);
128 while (1 < matches.size() && len < Constants.OBJECT_ID_STRING_LENGTH) {
129 abbrev = objectId.abbreviate(++len);
130 List<ObjectId> n = new ArrayList<ObjectId>(8);
131 for (ObjectId candidate : matches) {
132 if (abbrev.prefixCompare(candidate) == 0)
133 n.add(candidate);
134 }
135 if (1 < n.size())
136 matches = n;
137 else
138 matches = resolve(abbrev);
139 }
140 return abbrev;
141 }
142
143 /**
144 * Resolve an abbreviated ObjectId to its full form.
145 *
146 * This method searches for an ObjectId that begins with the abbreviation,
147 * and returns at least some matching candidates.
148 *
149 * If the returned collection is empty, no objects start with this
150 * abbreviation. The abbreviation doesn't belong to this repository, or the
151 * repository lacks the necessary objects to complete it.
152 *
153 * If the collection contains exactly one member, the abbreviation is
154 * (currently) unique within this database. There is a reasonably high
155 * probability that the returned id is what was previously abbreviated.
156 *
157 * If the collection contains 2 or more members, the abbreviation is not
158 * unique. In this case the implementation is only required to return at
159 * least 2 candidates to signal the abbreviation has conflicts. User
160 * friendly implementations should return as many candidates as reasonably
161 * possible, as the caller may be able to disambiguate further based on
162 * context. However since databases can be very large (e.g. 10 million
163 * objects) returning 625,000 candidates for the abbreviation "0" is simply
164 * unreasonable, so implementors should draw the line at around 256 matches.
165 *
166 * @param id
167 * abbreviated id to resolve to a complete identity. The
168 * abbreviation must have a length of at least 2.
169 * @return candidates that begin with the abbreviated identity.
170 * @throws IOException
171 * the object store cannot be read.
172 */
173 public abstract Collection<ObjectId> resolve(AbbreviatedObjectId id)
174 throws IOException;
175
176 /**
177 * Does the requested object exist in this database?
178 *
179 * @param objectId
180 * identity of the object to test for existence of.
181 * @return true if the specified object is stored in this database.
182 * @throws IOException
183 * the object store cannot be accessed.
184 */
185 public boolean has(AnyObjectId objectId) throws IOException {
186 return has(objectId, OBJ_ANY);
187 }
188
189 /**
190 * Does the requested object exist in this database?
191 *
192 * @param objectId
193 * identity of the object to test for existence of.
194 * @param typeHint
195 * hint about the type of object being requested, e.g.
196 * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
197 * type is not known, or does not matter to the caller.
198 * @return true if the specified object is stored in this database.
199 * @throws IncorrectObjectTypeException
200 * typeHint was not OBJ_ANY, and the object's actual type does
201 * not match typeHint.
202 * @throws IOException
203 * the object store cannot be accessed.
204 */
205 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
206 try {
207 open(objectId, typeHint);
208 return true;
209 } catch (MissingObjectException notFound) {
210 return false;
211 }
212 }
213
214 /**
215 * Open an object from this database.
216 *
217 * @param objectId
218 * identity of the object to open.
219 * @return a {@link ObjectLoader} for accessing the object.
220 * @throws MissingObjectException
221 * the object does not exist.
222 * @throws IOException
223 * the object store cannot be accessed.
224 */
225 public ObjectLoader open(AnyObjectId objectId)
226 throws MissingObjectException, IOException {
227 return open(objectId, OBJ_ANY);
228 }
229
230 /**
231 * Open an object from this database.
232 *
233 * @param objectId
234 * identity of the object to open.
235 * @param typeHint
236 * hint about the type of object being requested, e.g.
237 * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
238 * type is not known, or does not matter to the caller.
239 * @return a {@link ObjectLoader} for accessing the object.
240 * @throws MissingObjectException
241 * the object does not exist.
242 * @throws IncorrectObjectTypeException
243 * typeHint was not OBJ_ANY, and the object's actual type does
244 * not match typeHint.
245 * @throws IOException
246 * the object store cannot be accessed.
247 */
248 public abstract ObjectLoader open(AnyObjectId objectId, int typeHint)
249 throws MissingObjectException, IncorrectObjectTypeException,
250 IOException;
251
252 /**
253 * Returns IDs for those commits which should be considered as shallow.
254 *
255 * @return IDs of shallow commits
256 * @throws IOException
257 */
258 public abstract Set<ObjectId> getShallowCommits() throws IOException;
259
260 /**
261 * Asynchronous object opening.
262 *
263 * @param <T>
264 * type of identifier being supplied.
265 * @param objectIds
266 * objects to open from the object store. The supplied collection
267 * must not be modified until the queue has finished.
268 * @param reportMissing
269 * if true missing objects are reported by calling failure with a
270 * MissingObjectException. This may be more expensive for the
271 * implementation to guarantee. If false the implementation may
272 * choose to report MissingObjectException, or silently skip over
273 * the object with no warning.
274 * @return queue to read the objects from.
275 */
276 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
277 Iterable<T> objectIds, final boolean reportMissing) {
278 final Iterator<T> idItr = objectIds.iterator();
279 return new AsyncObjectLoaderQueue<T>() {
280 private T cur;
281
282 public boolean next() throws MissingObjectException, IOException {
283 if (idItr.hasNext()) {
284 cur = idItr.next();
285 return true;
286 } else {
287 return false;
288 }
289 }
290
291 public T getCurrent() {
292 return cur;
293 }
294
295 public ObjectId getObjectId() {
296 return cur;
297 }
298
299 public ObjectLoader open() throws IOException {
300 return ObjectReader.this.open(cur, OBJ_ANY);
301 }
302
303 public boolean cancel(boolean mayInterruptIfRunning) {
304 return true;
305 }
306
307 public void release() {
308 // Since we are sequential by default, we don't
309 // have any state to clean up if we terminate early.
310 }
311 };
312 }
313
314 /**
315 * Get only the size of an object.
316 * <p>
317 * The default implementation of this method opens an ObjectLoader.
318 * Databases are encouraged to override this if a faster access method is
319 * available to them.
320 *
321 * @param objectId
322 * identity of the object to open.
323 * @param typeHint
324 * hint about the type of object being requested, e.g.
325 * {@link Constants#OBJ_BLOB}; {@link #OBJ_ANY} if the object
326 * type is not known, or does not matter to the caller.
327 * @return size of object in bytes.
328 * @throws MissingObjectException
329 * the object does not exist.
330 * @throws IncorrectObjectTypeException
331 * typeHint was not OBJ_ANY, and the object's actual type does
332 * not match typeHint.
333 * @throws IOException
334 * the object store cannot be accessed.
335 */
336 public long getObjectSize(AnyObjectId objectId, int typeHint)
337 throws MissingObjectException, IncorrectObjectTypeException,
338 IOException {
339 return open(objectId, typeHint).getSize();
340 }
341
342 /**
343 * Asynchronous object size lookup.
344 *
345 * @param <T>
346 * type of identifier being supplied.
347 * @param objectIds
348 * objects to get the size of from the object store. The supplied
349 * collection must not be modified until the queue has finished.
350 * @param reportMissing
351 * if true missing objects are reported by calling failure with a
352 * MissingObjectException. This may be more expensive for the
353 * implementation to guarantee. If false the implementation may
354 * choose to report MissingObjectException, or silently skip over
355 * the object with no warning.
356 * @return queue to read object sizes from.
357 */
358 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
359 Iterable<T> objectIds, final boolean reportMissing) {
360 final Iterator<T> idItr = objectIds.iterator();
361 return new AsyncObjectSizeQueue<T>() {
362 private T cur;
363
364 private long sz;
365
366 public boolean next() throws MissingObjectException, IOException {
367 if (idItr.hasNext()) {
368 cur = idItr.next();
369 sz = getObjectSize(cur, OBJ_ANY);
370 return true;
371 } else {
372 return false;
373 }
374 }
375
376 public T getCurrent() {
377 return cur;
378 }
379
380 public ObjectId getObjectId() {
381 return cur;
382 }
383
384 public long getSize() {
385 return sz;
386 }
387
388 public boolean cancel(boolean mayInterruptIfRunning) {
389 return true;
390 }
391
392 public void release() {
393 // Since we are sequential by default, we don't
394 // have any state to clean up if we terminate early.
395 }
396 };
397 }
398
399 /**
400 * Advise the reader to avoid unreachable objects.
401 * <p>
402 * While enabled the reader will skip over anything previously proven to be
403 * unreachable. This may be dangerous in the face of concurrent writes.
404 *
405 * @param avoid
406 * true to avoid unreachable objects.
407 * @since 3.0
408 */
409 public void setAvoidUnreachableObjects(boolean avoid) {
410 // Do nothing by default.
411 }
412
413 /**
414 * An index that can be used to speed up ObjectWalks.
415 *
416 * @return the index or null if one does not exist.
417 * @throws IOException
418 * when the index fails to load
419 * @since 3.0
420 */
421 public BitmapIndex getBitmapIndex() throws IOException {
422 return null;
423 }
424
425 /**
426 * @return the {@link ObjectInserter} from which this reader was created
427 * using {@code inserter.newReader()}, or null if this reader was not
428 * created from an inserter.
429 * @since 4.4
430 */
431 @Nullable
432 public ObjectInserter getCreatedFromInserter() {
433 return null;
434 }
435
436 /**
437 * Release any resources used by this reader.
438 * <p>
439 * A reader that has been released can be used again, but may need to be
440 * released after the subsequent usage.
441 *
442 * @since 4.0
443 */
444 @Override
445 public abstract void close();
446
447 /**
448 * Wraps a delegate ObjectReader.
449 *
450 * @since 4.4
451 */
452 public static abstract class Filter extends ObjectReader {
453 /**
454 * @return delegate ObjectReader to handle all processing.
455 * @since 4.4
456 */
457 protected abstract ObjectReader delegate();
458
459 @Override
460 public ObjectReader newReader() {
461 return delegate().newReader();
462 }
463
464 @Override
465 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
466 throws IOException {
467 return delegate().abbreviate(objectId);
468 }
469
470 @Override
471 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
472 throws IOException {
473 return delegate().abbreviate(objectId, len);
474 }
475
476 @Override
477 public Collection<ObjectId> resolve(AbbreviatedObjectId id)
478 throws IOException {
479 return delegate().resolve(id);
480 }
481
482 @Override
483 public boolean has(AnyObjectId objectId) throws IOException {
484 return delegate().has(objectId);
485 }
486
487 @Override
488 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
489 return delegate().has(objectId, typeHint);
490 }
491
492 @Override
493 public ObjectLoader open(AnyObjectId objectId)
494 throws MissingObjectException, IOException {
495 return delegate().open(objectId);
496 }
497
498 @Override
499 public ObjectLoader open(AnyObjectId objectId, int typeHint)
500 throws MissingObjectException, IncorrectObjectTypeException,
501 IOException {
502 return delegate().open(objectId, typeHint);
503 }
504
505 @Override
506 public Set<ObjectId> getShallowCommits() throws IOException {
507 return delegate().getShallowCommits();
508 }
509
510 @Override
511 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
512 Iterable<T> objectIds, boolean reportMissing) {
513 return delegate().open(objectIds, reportMissing);
514 }
515
516 @Override
517 public long getObjectSize(AnyObjectId objectId, int typeHint)
518 throws MissingObjectException, IncorrectObjectTypeException,
519 IOException {
520 return delegate().getObjectSize(objectId, typeHint);
521 }
522
523 @Override
524 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
525 Iterable<T> objectIds, boolean reportMissing) {
526 return delegate().getObjectSize(objectIds, reportMissing);
527 }
528
529 @Override
530 public void setAvoidUnreachableObjects(boolean avoid) {
531 delegate().setAvoidUnreachableObjects(avoid);
532 }
533
534 @Override
535 public BitmapIndex getBitmapIndex() throws IOException {
536 return delegate().getBitmapIndex();
537 }
538
539 @Override
540 @Nullable
541 public ObjectInserter getCreatedFromInserter() {
542 return delegate().getCreatedFromInserter();
543 }
544
545 @Override
546 public void close() {
547 delegate().close();
548 }
549 }
550 }