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