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
57 /**
58 * Reads an {@link org.eclipse.jgit.lib.ObjectDatabase} for a single thread.
59 * <p>
60 * Readers that can support efficient reuse of pack encoded objects should also
61 * implement the companion interface
62 * {@link org.eclipse.jgit.internal.storage.pack.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 * The threshold at which a file will be streamed rather than loaded
70 * entirely into memory.
71 * @since 4.6
72 */
73 protected int streamFileThreshold;
74
75 /**
76 * Construct a new reader from the same data.
77 * <p>
78 * Applications can use this method to build a new reader from the same data
79 * source, but for an different thread.
80 *
81 * @return a brand new reader, using the same data source.
82 */
83 public abstract ObjectReader newReader();
84
85 /**
86 * Obtain a unique abbreviation (prefix) of an object SHA-1.
87 *
88 * This method uses a reasonable default for the minimum length. Callers who
89 * don't care about the minimum length should prefer this method.
90 *
91 * The returned abbreviation would expand back to the argument ObjectId when
92 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
93 * are added to this repository between calls.
94 *
95 * @param objectId
96 * object identity that needs to be abbreviated.
97 * @return SHA-1 abbreviation.
98 * @throws java.io.IOException
99 * the object store cannot be read.
100 */
101 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
102 throws IOException {
103 return abbreviate(objectId, 7);
104 }
105
106 /**
107 * Obtain a unique abbreviation (prefix) of an object SHA-1.
108 *
109 * The returned abbreviation would expand back to the argument ObjectId when
110 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
111 * are added to this repository between calls.
112 *
113 * The default implementation of this method abbreviates the id to the
114 * minimum length, then resolves it to see if there are multiple results.
115 * When multiple results are found, the length is extended by 1 and resolve
116 * is tried again.
117 *
118 * @param objectId
119 * object identity that needs to be abbreviated.
120 * @param len
121 * minimum length of the abbreviated string. Must be in the range
122 * [2, {@value Constants#OBJECT_ID_STRING_LENGTH}].
123 * @return SHA-1 abbreviation. If no matching objects exist in the
124 * repository, the abbreviation will match the minimum length.
125 * @throws java.io.IOException
126 * the object store cannot be read.
127 */
128 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
129 throws IOException {
130 if (len == Constants.OBJECT_ID_STRING_LENGTH)
131 return AbbreviatedObjectId.fromObjectId(objectId);
132
133 AbbreviatedObjectId abbrev = objectId.abbreviate(len);
134 Collection<ObjectId> matches = resolve(abbrev);
135 while (1 < matches.size() && len < Constants.OBJECT_ID_STRING_LENGTH) {
136 abbrev = objectId.abbreviate(++len);
137 List<ObjectId> n = new ArrayList<>(8);
138 for (ObjectId candidate : matches) {
139 if (abbrev.prefixCompare(candidate) == 0)
140 n.add(candidate);
141 }
142 if (1 < n.size())
143 matches = n;
144 else
145 matches = resolve(abbrev);
146 }
147 return abbrev;
148 }
149
150 /**
151 * Resolve an abbreviated ObjectId to its full form.
152 *
153 * This method searches for an ObjectId that begins with the abbreviation,
154 * and returns at least some matching candidates.
155 *
156 * If the returned collection is empty, no objects start with this
157 * abbreviation. The abbreviation doesn't belong to this repository, or the
158 * repository lacks the necessary objects to complete it.
159 *
160 * If the collection contains exactly one member, the abbreviation is
161 * (currently) unique within this database. There is a reasonably high
162 * probability that the returned id is what was previously abbreviated.
163 *
164 * If the collection contains 2 or more members, the abbreviation is not
165 * unique. In this case the implementation is only required to return at
166 * least 2 candidates to signal the abbreviation has conflicts. User
167 * friendly implementations should return as many candidates as reasonably
168 * possible, as the caller may be able to disambiguate further based on
169 * context. However since databases can be very large (e.g. 10 million
170 * objects) returning 625,000 candidates for the abbreviation "0" is simply
171 * unreasonable, so implementors should draw the line at around 256 matches.
172 *
173 * @param id
174 * abbreviated id to resolve to a complete identity. The
175 * abbreviation must have a length of at least 2.
176 * @return candidates that begin with the abbreviated identity.
177 * @throws java.io.IOException
178 * the object store cannot be read.
179 */
180 public abstract Collection<ObjectId> resolve(AbbreviatedObjectId id)
181 throws IOException;
182
183 /**
184 * Does the requested object exist in this database?
185 *
186 * @param objectId
187 * identity of the object to test for existence of.
188 * @return true if the specified object is stored in this database.
189 * @throws java.io.IOException
190 * the object store cannot be accessed.
191 */
192 public boolean has(AnyObjectId objectId) throws IOException {
193 return has(objectId, OBJ_ANY);
194 }
195
196 /**
197 * Does the requested object exist in this database?
198 *
199 * @param objectId
200 * identity of the object to test for existence of.
201 * @param typeHint
202 * hint about the type of object being requested, e.g.
203 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
204 * {@link #OBJ_ANY} if the object type is not known, or does not
205 * matter to the caller.
206 * @return true if the specified object is stored in this database.
207 * @throws IncorrectObjectTypeException
208 * typeHint was not OBJ_ANY, and the object's actual type does
209 * not match typeHint.
210 * @throws java.io.IOException
211 * the object store cannot be accessed.
212 */
213 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
214 try {
215 open(objectId, typeHint);
216 return true;
217 } catch (MissingObjectException notFound) {
218 return false;
219 }
220 }
221
222 /**
223 * Open an object from this database.
224 *
225 * @param objectId
226 * identity of the object to open.
227 * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
228 * object.
229 * @throws org.eclipse.jgit.errors.MissingObjectException
230 * the object does not exist.
231 * @throws java.io.IOException
232 * the object store cannot be accessed.
233 */
234 public ObjectLoader open(AnyObjectId objectId)
235 throws MissingObjectException, IOException {
236 return open(objectId, OBJ_ANY);
237 }
238
239 /**
240 * Open an object from this database.
241 *
242 * @param objectId
243 * identity of the object to open.
244 * @param typeHint
245 * hint about the type of object being requested, e.g.
246 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
247 * {@link #OBJ_ANY} if the object type is not known, or does not
248 * matter to the caller.
249 * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
250 * object.
251 * @throws org.eclipse.jgit.errors.MissingObjectException
252 * the object does not exist.
253 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
254 * typeHint was not OBJ_ANY, and the object's actual type does
255 * not match typeHint.
256 * @throws java.io.IOException
257 * the object store cannot be accessed.
258 */
259 public abstract ObjectLoader open(AnyObjectId objectId, int typeHint)
260 throws MissingObjectException, IncorrectObjectTypeException,
261 IOException;
262
263 /**
264 * Returns IDs for those commits which should be considered as shallow.
265 *
266 * @return IDs of shallow commits
267 * @throws java.io.IOException
268 */
269 public abstract Set<ObjectId> getShallowCommits() throws IOException;
270
271 /**
272 * Asynchronous object opening.
273 *
274 * @param objectIds
275 * objects to open from the object store. The supplied collection
276 * must not be modified until the queue has finished.
277 * @param reportMissing
278 * if true missing objects are reported by calling failure with a
279 * MissingObjectException. This may be more expensive for the
280 * implementation to guarantee. If false the implementation may
281 * choose to report MissingObjectException, or silently skip over
282 * the object with no warning.
283 * @return queue to read the objects from.
284 */
285 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
286 Iterable<T> objectIds, final boolean reportMissing) {
287 final Iterator<T> idItr = objectIds.iterator();
288 return new AsyncObjectLoaderQueue<T>() {
289 private T cur;
290
291 @Override
292 public boolean next() throws MissingObjectException, IOException {
293 if (idItr.hasNext()) {
294 cur = idItr.next();
295 return true;
296 } else {
297 return false;
298 }
299 }
300
301 @Override
302 public T getCurrent() {
303 return cur;
304 }
305
306 @Override
307 public ObjectId getObjectId() {
308 return cur;
309 }
310
311 @Override
312 public ObjectLoader open() throws IOException {
313 return ObjectReader.this.open(cur, OBJ_ANY);
314 }
315
316 @Override
317 public boolean cancel(boolean mayInterruptIfRunning) {
318 return true;
319 }
320
321 @Override
322 public void release() {
323 // Since we are sequential by default, we don't
324 // have any state to clean up if we terminate early.
325 }
326 };
327 }
328
329 /**
330 * Get only the size of an object.
331 * <p>
332 * The default implementation of this method opens an ObjectLoader.
333 * Databases are encouraged to override this if a faster access method is
334 * available to them.
335 *
336 * @param objectId
337 * identity of the object to open.
338 * @param typeHint
339 * hint about the type of object being requested, e.g.
340 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
341 * {@link #OBJ_ANY} if the object type is not known, or does not
342 * matter to the caller.
343 * @return size of object in bytes.
344 * @throws org.eclipse.jgit.errors.MissingObjectException
345 * the object does not exist.
346 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
347 * typeHint was not OBJ_ANY, and the object's actual type does
348 * not match typeHint.
349 * @throws java.io.IOException
350 * the object store cannot be accessed.
351 */
352 public long getObjectSize(AnyObjectId objectId, int typeHint)
353 throws MissingObjectException, IncorrectObjectTypeException,
354 IOException {
355 return open(objectId, typeHint).getSize();
356 }
357
358 /**
359 * Asynchronous object size lookup.
360 *
361 * @param objectIds
362 * objects to get the size of from the object store. The supplied
363 * collection must not be modified until the queue has finished.
364 * @param reportMissing
365 * if true missing objects are reported by calling failure with a
366 * MissingObjectException. This may be more expensive for the
367 * implementation to guarantee. If false the implementation may
368 * choose to report MissingObjectException, or silently skip over
369 * the object with no warning.
370 * @return queue to read object sizes from.
371 */
372 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
373 Iterable<T> objectIds, final boolean reportMissing) {
374 final Iterator<T> idItr = objectIds.iterator();
375 return new AsyncObjectSizeQueue<T>() {
376 private T cur;
377
378 private long sz;
379
380 @Override
381 public boolean next() throws MissingObjectException, IOException {
382 if (idItr.hasNext()) {
383 cur = idItr.next();
384 sz = getObjectSize(cur, OBJ_ANY);
385 return true;
386 } else {
387 return false;
388 }
389 }
390
391 @Override
392 public T getCurrent() {
393 return cur;
394 }
395
396 @Override
397 public ObjectId getObjectId() {
398 return cur;
399 }
400
401 @Override
402 public long getSize() {
403 return sz;
404 }
405
406 @Override
407 public boolean cancel(boolean mayInterruptIfRunning) {
408 return true;
409 }
410
411 @Override
412 public void release() {
413 // Since we are sequential by default, we don't
414 // have any state to clean up if we terminate early.
415 }
416 };
417 }
418
419 /**
420 * Advise the reader to avoid unreachable objects.
421 * <p>
422 * While enabled the reader will skip over anything previously proven to be
423 * unreachable. This may be dangerous in the face of concurrent writes.
424 *
425 * @param avoid
426 * true to avoid unreachable objects.
427 * @since 3.0
428 */
429 public void setAvoidUnreachableObjects(boolean avoid) {
430 // Do nothing by default.
431 }
432
433 /**
434 * An index that can be used to speed up ObjectWalks.
435 *
436 * @return the index or null if one does not exist.
437 * @throws java.io.IOException
438 * when the index fails to load
439 * @since 3.0
440 */
441 public BitmapIndex getBitmapIndex() throws IOException {
442 return null;
443 }
444
445 /**
446 * Get the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
447 * reader was created using {@code inserter.newReader()}
448 *
449 * @return the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
450 * reader was created using {@code inserter.newReader()}, or null if
451 * this reader was not created from an inserter.
452 * @since 4.4
453 */
454 @Nullable
455 public ObjectInserter getCreatedFromInserter() {
456 return null;
457 }
458
459 /**
460 * {@inheritDoc}
461 * <p>
462 * Release any resources used by this reader.
463 * <p>
464 * A reader that has been released can be used again, but may need to be
465 * released after the subsequent usage.
466 *
467 * @since 4.0
468 */
469 @Override
470 public abstract void close();
471
472 /**
473 * Sets the threshold at which a file will be streamed rather than loaded
474 * entirely into memory
475 *
476 * @param threshold
477 * the new threshold
478 * @since 4.6
479 */
480 public void setStreamFileThreshold(int threshold) {
481 streamFileThreshold = threshold;
482 }
483
484 /**
485 * Returns the threshold at which a file will be streamed rather than loaded
486 * entirely into memory
487 *
488 * @return the threshold in bytes
489 * @since 4.6
490 */
491 public int getStreamFileThreshold() {
492 return streamFileThreshold;
493 }
494
495 /**
496 * Wraps a delegate ObjectReader.
497 *
498 * @since 4.4
499 */
500 public static abstract class Filter extends ObjectReader {
501 /**
502 * @return delegate ObjectReader to handle all processing.
503 * @since 4.4
504 */
505 protected abstract ObjectReader delegate();
506
507 @Override
508 public ObjectReader newReader() {
509 return delegate().newReader();
510 }
511
512 @Override
513 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
514 throws IOException {
515 return delegate().abbreviate(objectId);
516 }
517
518 @Override
519 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
520 throws IOException {
521 return delegate().abbreviate(objectId, len);
522 }
523
524 @Override
525 public Collection<ObjectId> resolve(AbbreviatedObjectId id)
526 throws IOException {
527 return delegate().resolve(id);
528 }
529
530 @Override
531 public boolean has(AnyObjectId objectId) throws IOException {
532 return delegate().has(objectId);
533 }
534
535 @Override
536 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
537 return delegate().has(objectId, typeHint);
538 }
539
540 @Override
541 public ObjectLoader open(AnyObjectId objectId)
542 throws MissingObjectException, IOException {
543 return delegate().open(objectId);
544 }
545
546 @Override
547 public ObjectLoader open(AnyObjectId objectId, int typeHint)
548 throws MissingObjectException, IncorrectObjectTypeException,
549 IOException {
550 return delegate().open(objectId, typeHint);
551 }
552
553 @Override
554 public Set<ObjectId> getShallowCommits() throws IOException {
555 return delegate().getShallowCommits();
556 }
557
558 @Override
559 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
560 Iterable<T> objectIds, boolean reportMissing) {
561 return delegate().open(objectIds, reportMissing);
562 }
563
564 @Override
565 public long getObjectSize(AnyObjectId objectId, int typeHint)
566 throws MissingObjectException, IncorrectObjectTypeException,
567 IOException {
568 return delegate().getObjectSize(objectId, typeHint);
569 }
570
571 @Override
572 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
573 Iterable<T> objectIds, boolean reportMissing) {
574 return delegate().getObjectSize(objectIds, reportMissing);
575 }
576
577 @Override
578 public void setAvoidUnreachableObjects(boolean avoid) {
579 delegate().setAvoidUnreachableObjects(avoid);
580 }
581
582 @Override
583 public BitmapIndex getBitmapIndex() throws IOException {
584 return delegate().getBitmapIndex();
585 }
586
587 @Override
588 @Nullable
589 public ObjectInserter getCreatedFromInserter() {
590 return delegate().getCreatedFromInserter();
591 }
592
593 @Override
594 public void close() {
595 delegate().close();
596 }
597 }
598 }