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 }