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