View Javadoc
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 }