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 }