Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgDataFile.java @ 425:48f993aa2f41
FIXMEs: exceptions, javadoc
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 28 Mar 2012 18:39:29 +0200 |
parents | 6437d261048a |
children | 063b0663495a |
comparison
equal
deleted
inserted
replaced
424:6437d261048a | 425:48f993aa2f41 |
---|---|
27 import java.nio.channels.FileChannel; | 27 import java.nio.channels.FileChannel; |
28 import java.util.ArrayList; | 28 import java.util.ArrayList; |
29 import java.util.Arrays; | 29 import java.util.Arrays; |
30 import java.util.Collection; | 30 import java.util.Collection; |
31 | 31 |
32 import org.tmatesoft.hg.core.HgException; | 32 import org.tmatesoft.hg.core.HgChangesetFileSneaker; |
33 import org.tmatesoft.hg.core.Nodeid; | 33 import org.tmatesoft.hg.core.Nodeid; |
34 import org.tmatesoft.hg.internal.DataAccess; | 34 import org.tmatesoft.hg.internal.DataAccess; |
35 import org.tmatesoft.hg.internal.FilterByteChannel; | 35 import org.tmatesoft.hg.internal.FilterByteChannel; |
36 import org.tmatesoft.hg.internal.FilterDataAccess; | 36 import org.tmatesoft.hg.internal.FilterDataAccess; |
37 import org.tmatesoft.hg.internal.IntMap; | 37 import org.tmatesoft.hg.internal.IntMap; |
46 import org.tmatesoft.hg.util.ProgressSupport; | 46 import org.tmatesoft.hg.util.ProgressSupport; |
47 | 47 |
48 | 48 |
49 | 49 |
50 /** | 50 /** |
51 * ? name:HgFileNode? | 51 * Regular user data file stored in the repository. |
52 * | |
53 * <p> Note, most methods accept index in the file's revision history, not that of changelog. Easy way to obtain | |
54 * changeset revision index from file's is to use {@link #getChangesetRevisionIndex(int)}. To obtain file's revision | |
55 * index for a given changeset, {@link HgManifest#getFileRevision(int, Path)} or {@link HgChangesetFileSneaker} may | |
56 * come handy. | |
52 * | 57 * |
53 * @author Artem Tikhomirov | 58 * @author Artem Tikhomirov |
54 * @author TMate Software Ltd. | 59 * @author TMate Software Ltd. |
55 */ | 60 */ |
56 public class HgDataFile extends Revlog { | 61 public class HgDataFile extends Revlog { |
86 * Handy shorthand for {@link #getLength(int) length(getRevisionIndex(nodeid))} | 91 * Handy shorthand for {@link #getLength(int) length(getRevisionIndex(nodeid))} |
87 * | 92 * |
88 * @param nodeid revision of the file | 93 * @param nodeid revision of the file |
89 * | 94 * |
90 * @return size of the file content at the given revision | 95 * @return size of the file content at the given revision |
91 * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog (<em>runtime exception</em>) | 96 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
92 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 97 */ |
93 */ | 98 public int getLength(Nodeid nodeid) throws HgRuntimeException { |
94 public int getLength(Nodeid nodeid) throws HgInvalidControlFileException, HgInvalidRevisionException { | |
95 try { | 99 try { |
96 return getLength(getRevisionIndex(nodeid)); | 100 return getLength(getRevisionIndex(nodeid)); |
97 } catch (HgInvalidControlFileException ex) { | 101 } catch (HgInvalidControlFileException ex) { |
98 throw ex.isRevisionSet() ? ex : ex.setRevision(nodeid); | 102 throw ex.isRevisionSet() ? ex : ex.setRevision(nodeid); |
99 } catch (HgInvalidRevisionException ex) { | 103 } catch (HgInvalidRevisionException ex) { |
102 } | 106 } |
103 | 107 |
104 /** | 108 /** |
105 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, only {@link HgRepository#TIP} makes sense. | 109 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, only {@link HgRepository#TIP} makes sense. |
106 * @return size of the file content at the revision identified by local revision number. | 110 * @return size of the file content at the revision identified by local revision number. |
107 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) | 111 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
108 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 112 */ |
109 */ | 113 public int getLength(int fileRevisionIndex) throws HgRuntimeException { |
110 public int getLength(int fileRevisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { | |
111 if (wrongRevisionIndex(fileRevisionIndex) || fileRevisionIndex == BAD_REVISION) { | 114 if (wrongRevisionIndex(fileRevisionIndex) || fileRevisionIndex == BAD_REVISION) { |
112 throw new HgInvalidRevisionException(fileRevisionIndex); | 115 throw new HgInvalidRevisionException(fileRevisionIndex); |
113 } | 116 } |
114 if (fileRevisionIndex == TIP) { | 117 if (fileRevisionIndex == TIP) { |
115 fileRevisionIndex = getLastRevision(); | 118 fileRevisionIndex = getLastRevision(); |
143 * | 146 * |
144 * NOTE, if file is missing from the working directory and is not part of the dirstate (but otherwise legal repository file, | 147 * NOTE, if file is missing from the working directory and is not part of the dirstate (but otherwise legal repository file, |
145 * e.g. from another branch), no content would be supplied. | 148 * e.g. from another branch), no content would be supplied. |
146 * | 149 * |
147 * @param sink content consumer | 150 * @param sink content consumer |
148 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
149 * @throws HgInvalidFileException if access to file in working directory failed | |
150 * @throws CancelledException if execution of the operation was cancelled | 151 * @throws CancelledException if execution of the operation was cancelled |
151 */ | 152 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
152 public void workingCopy(ByteChannel sink) throws HgException, CancelledException { | 153 */ |
154 public void workingCopy(ByteChannel sink) throws CancelledException, HgRuntimeException { | |
153 File f = getRepo().getFile(this); | 155 File f = getRepo().getFile(this); |
154 if (f.exists()) { | 156 if (f.exists()) { |
155 final CancelSupport cs = CancelSupport.Factory.get(sink); | 157 final CancelSupport cs = CancelSupport.Factory.get(sink); |
156 final ProgressSupport progress = ProgressSupport.Factory.get(sink); | 158 final ProgressSupport progress = ProgressSupport.Factory.get(sink); |
157 final long flength = f.length(); | 159 final long flength = f.length(); |
232 * XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves? | 234 * XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves? |
233 * | 235 * |
234 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. | 236 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. |
235 * @param sink content consumer | 237 * @param sink content consumer |
236 * | 238 * |
237 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
238 * @throws HgInvalidFileException if access to file in working directory failed | |
239 * @throws CancelledException if execution of the operation was cancelled | 239 * @throws CancelledException if execution of the operation was cancelled |
240 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) | 240 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
241 */ | 241 */ |
242 public void contentWithFilters(int fileRevisionIndex, ByteChannel sink) throws HgException, CancelledException, HgInvalidRevisionException { | 242 public void contentWithFilters(int fileRevisionIndex, ByteChannel sink) throws CancelledException, HgRuntimeException { |
243 if (fileRevisionIndex == WORKING_COPY) { | 243 if (fileRevisionIndex == WORKING_COPY) { |
244 workingCopy(sink); // pass un-mangled sink | 244 workingCopy(sink); // pass un-mangled sink |
245 } else { | 245 } else { |
246 content(fileRevisionIndex, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath()))); | 246 content(fileRevisionIndex, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath()))); |
247 } | 247 } |
252 * For filtered content, use {@link #contentWithFilters(int, ByteChannel)}. | 252 * For filtered content, use {@link #contentWithFilters(int, ByteChannel)}. |
253 * | 253 * |
254 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. | 254 * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. |
255 * @param sink content consumer | 255 * @param sink content consumer |
256 * | 256 * |
257 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
258 * @throws HgInvalidFileException if access to file in working directory failed | |
259 * @throws CancelledException if execution of the operation was cancelled | 257 * @throws CancelledException if execution of the operation was cancelled |
260 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) | 258 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
261 */ | 259 */ |
262 public void content(int fileRevisionIndex, ByteChannel sink) throws HgException, CancelledException, HgInvalidRevisionException { | 260 public void content(int fileRevisionIndex, ByteChannel sink) throws CancelledException, HgRuntimeException { |
263 // for data files need to check heading of the file content for possible metadata | 261 // for data files need to check heading of the file content for possible metadata |
264 // @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8- | 262 // @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8- |
265 if (fileRevisionIndex == TIP) { | 263 if (fileRevisionIndex == TIP) { |
266 fileRevisionIndex = getLastRevision(); | 264 fileRevisionIndex = getLastRevision(); |
267 } | 265 } |
291 insp = new MetadataInspector(metadata, lf, new ContentPipe(sink, 0, lf)); | 289 insp = new MetadataInspector(metadata, lf, new ContentPipe(sink, 0, lf)); |
292 } | 290 } |
293 insp.checkCancelled(); | 291 insp.checkCancelled(); |
294 super.content.iterate(fileRevisionIndex, fileRevisionIndex, true, insp); | 292 super.content.iterate(fileRevisionIndex, fileRevisionIndex, true, insp); |
295 try { | 293 try { |
296 insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe? | 294 insp.checkFailed(); |
297 } catch (HgInvalidControlFileException ex) { | 295 } catch (HgInvalidControlFileException ex) { |
298 ex = ex.setFileName(getPath()); | 296 ex = ex.setFileName(getPath()); |
299 throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(fileRevisionIndex); | 297 throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(fileRevisionIndex); |
300 } catch (IOException ex) { | 298 } catch (IOException ex) { |
301 HgInvalidControlFileException e = new HgInvalidControlFileException("Revision content access failed", ex, null); | 299 HgInvalidControlFileException e = new HgInvalidControlFileException("Revision content access failed", ex, null); |
302 throw content.initWithIndexFile(e).setFileName(getPath()).setRevisionIndex(fileRevisionIndex); | 300 throw content.initWithIndexFile(e).setFileName(getPath()).setRevisionIndex(fileRevisionIndex); |
303 } catch (HgException ex) { | 301 } |
304 // shall not happen, unless we changed ContentPipe or its subclass | 302 } |
305 HgInvalidControlFileException e = new HgInvalidControlFileException("Revision content access failed", ex, null); | 303 |
306 throw content.initWithIndexFile(e).setFileName(getPath()).setRevisionIndex(fileRevisionIndex); | 304 /** |
307 } | 305 * Walk complete change history of the file. |
308 } | 306 * @param inspector callback to visit changesets |
309 | 307 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
310 // FIXME is that still needed? | 308 */ |
311 public void history(HgChangelog.Inspector inspector) throws HgInvalidControlFileException { | 309 public void history(HgChangelog.Inspector inspector) throws HgRuntimeException { |
312 history(0, getLastRevision(), inspector); | 310 history(0, getLastRevision(), inspector); |
313 } | 311 } |
314 | 312 |
315 /** | 313 /** |
316 * | 314 * Walk subset of the file's change history. |
317 * @param start local revision index | 315 * @param start revision local index, inclusive; non-negative or {@link HgRepository#TIP} |
318 * @param end local revision index | 316 * @param end revision local index, inclusive; non-negative or {@link HgRepository#TIP} |
319 * @param inspector | 317 * @param inspector callback to visit changesets |
320 * FIXME EXCEPTIONS | 318 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
321 * @throws HgInvalidRevisionException | 319 */ |
322 * @throws HgInvalidControlFileException | 320 public void history(int start, int end, HgChangelog.Inspector inspector) throws HgRuntimeException { |
323 */ | |
324 public void history(int start, int end, HgChangelog.Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | |
325 if (!exists()) { | 321 if (!exists()) { |
326 throw new IllegalStateException("Can't get history of invalid repository file node"); | 322 throw new IllegalStateException("Can't get history of invalid repository file node"); |
327 } | 323 } |
328 final int last = getLastRevision(); | 324 final int last = getLastRevision(); |
329 if (end == TIP) { | 325 if (end == TIP) { |
359 | 355 |
360 /** | 356 /** |
361 * For a given revision of the file (identified with revision index), find out index of the corresponding changeset. | 357 * For a given revision of the file (identified with revision index), find out index of the corresponding changeset. |
362 * | 358 * |
363 * @return changeset revision index | 359 * @return changeset revision index |
364 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog | 360 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
365 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 361 */ |
366 */ | 362 public int getChangesetRevisionIndex(int revision) throws HgRuntimeException { |
367 public int getChangesetRevisionIndex(int revision) throws HgInvalidControlFileException, HgInvalidRevisionException { | |
368 return content.linkRevision(revision); | 363 return content.linkRevision(revision); |
369 } | 364 } |
370 | 365 |
371 /** | 366 /** |
372 * Complements {@link #getChangesetRevisionIndex(int)} to get changeset revision that corresponds to supplied file revision | 367 * Complements {@link #getChangesetRevisionIndex(int)} to get changeset revision that corresponds to supplied file revision |
373 * | 368 * |
374 * @param nid revision of the file | 369 * @param nid revision of the file |
375 * @return changeset revision | 370 * @return changeset revision |
376 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog | 371 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
377 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 372 */ |
378 */ | 373 public Nodeid getChangesetRevision(Nodeid nid) throws HgRuntimeException { |
379 public Nodeid getChangesetRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException { | |
380 int changelogRevision = getChangesetRevisionIndex(getRevisionIndex(nid)); | 374 int changelogRevision = getChangesetRevisionIndex(getRevisionIndex(nid)); |
381 return getRepo().getChangelog().getRevision(changelogRevision); | 375 return getRepo().getChangelog().getRevision(changelogRevision); |
382 } | 376 } |
383 | 377 |
384 /** | 378 /** |
385 * Tells whether this file originates from another repository file | 379 * Tells whether this file originates from another repository file |
386 * @return <code>true</code> if this file is a copy of another from the repository | 380 * @return <code>true</code> if this file is a copy of another from the repository |
387 * @throws HgInvalidControlFileException if access to revlog or file metadata failed | 381 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
388 */ | 382 */ |
389 public boolean isCopy() throws HgInvalidControlFileException { | 383 public boolean isCopy() throws HgRuntimeException { |
390 if (metadata == null || !metadata.checked(0)) { | 384 if (metadata == null || !metadata.checked(0)) { |
391 checkAndRecordMetadata(0); | 385 checkAndRecordMetadata(0); |
392 } | 386 } |
393 if (!metadata.known(0)) { | 387 if (!metadata.known(0)) { |
394 return false; | 388 return false; |
398 | 392 |
399 /** | 393 /** |
400 * Get name of the file this one was copied from. | 394 * Get name of the file this one was copied from. |
401 * | 395 * |
402 * @return name of the file origin | 396 * @return name of the file origin |
403 * @throws HgInvalidControlFileException if access to revlog or file metadata failed | 397 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
404 * @throws UnsupportedOperationException if this file doesn't represent a copy ({@link #isCopy()} was false) | 398 */ |
405 */ | 399 public Path getCopySourceName() throws HgRuntimeException { |
406 public Path getCopySourceName() throws HgInvalidControlFileException { | |
407 if (isCopy()) { | 400 if (isCopy()) { |
408 return Path.create(metadata.find(0, "copy")); | 401 return Path.create(metadata.find(0, "copy")); |
409 } | 402 } |
410 throw new UnsupportedOperationException(); // XXX REVISIT, think over if Exception is good (clients would check isCopy() anyway, perhaps null is sufficient?) | 403 throw new UnsupportedOperationException(); // XXX REVISIT, think over if Exception is good (clients would check isCopy() anyway, perhaps null is sufficient?) |
411 } | 404 } |
412 | 405 |
413 /** | 406 /** |
414 * | 407 * |
415 * @return revision this file was copied from | 408 * @return revision this file was copied from |
416 * @throws HgInvalidControlFileException if access to revlog or file metadata failed | 409 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
417 * @throws UnsupportedOperationException if this file doesn't represent a copy ({@link #isCopy()} was false) | 410 */ |
418 */ | 411 public Nodeid getCopySourceRevision() throws HgRuntimeException { |
419 public Nodeid getCopySourceRevision() throws HgInvalidControlFileException { | |
420 if (isCopy()) { | 412 if (isCopy()) { |
421 return Nodeid.fromAscii(metadata.find(0, "copyrev")); // XXX reuse/cache Nodeid | 413 return Nodeid.fromAscii(metadata.find(0, "copyrev")); // XXX reuse/cache Nodeid |
422 } | 414 } |
423 throw new UnsupportedOperationException(); | 415 throw new UnsupportedOperationException(); |
424 } | 416 } |
425 | 417 |
426 /** | 418 /** |
427 * Get file flags recorded in the manifest | 419 * Get file flags recorded in the manifest |
428 * @param fileRevisionIndex - revision local index, non-negative, or {@link HgRepository#TIP}. | 420 * @param fileRevisionIndex - revision local index, non-negative, or {@link HgRepository#TIP}. |
429 * @see HgManifest#getFileFlags(int, Path) | 421 * @see HgManifest#getFileFlags(int, Path) |
430 * FIXME EXCEPTIONS | 422 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
431 * @throws HgInvalidControlFileException | 423 */ |
432 * @throws HgInvalidRevisionException | 424 public HgManifest.Flags getFlags(int fileRevisionIndex) throws HgRuntimeException { |
433 */ | |
434 public HgManifest.Flags getFlags(int fileRevisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { | |
435 int changesetRevIndex = getChangesetRevisionIndex(fileRevisionIndex); | 425 int changesetRevIndex = getChangesetRevisionIndex(fileRevisionIndex); |
436 return getRepo().getManifest().getFileFlags(changesetRevIndex, getPath()); | 426 return getRepo().getManifest().getFileFlags(changesetRevIndex, getPath()); |
437 } | 427 } |
438 | 428 |
439 @Override | 429 @Override |
460 }); | 450 }); |
461 } catch (CancelledException ex) { | 451 } catch (CancelledException ex) { |
462 // it's ok, we did that | 452 // it's ok, we did that |
463 } catch (HgInvalidControlFileException ex) { | 453 } catch (HgInvalidControlFileException ex) { |
464 throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(localRev); | 454 throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(localRev); |
465 } catch (HgException ex) { | |
466 // metadata comes from the content, hence initWithDataFile | |
467 throw content.initWithDataFile(new HgInvalidControlFileException(null, ex, null)); | |
468 } | 455 } |
469 } | 456 } |
470 | 457 |
471 private static final class MetadataEntry { | 458 private static final class MetadataEntry { |
472 private final String entry; | 459 private final String entry; |
652 } | 639 } |
653 return lastEntryStart; | 640 return lastEntryStart; |
654 } | 641 } |
655 | 642 |
656 @Override | 643 @Override |
657 public void checkFailed() throws HgException, IOException, CancelledException { | 644 public void checkFailed() throws HgRuntimeException, IOException, CancelledException { |
658 super.checkFailed(); | 645 super.checkFailed(); |
659 if (delegate instanceof ErrorHandlingInspector) { | 646 if (delegate instanceof ErrorHandlingInspector) { |
660 // XXX need to add ErrorDestination and pass it around (much like CancelSupport get passed) | 647 // TODO need to add ErrorDestination (ErrorTarget/Acceptor?) and pass it around (much like CancelSupport get passed) |
661 // so that delegate would be able report its failures directly to caller without this hack | 648 // so that delegate would be able report its failures directly to caller without this hack |
662 ((ErrorHandlingInspector) delegate).checkFailed(); | 649 ((ErrorHandlingInspector) delegate).checkFailed(); |
663 } | 650 } |
664 } | 651 } |
665 } | 652 } |