Mercurial > hg4j
diff src/org/tmatesoft/hg/repo/HgDataFile.java @ 396:0ae53c32ecef
Straighten out exceptions thrown when file access failed - three is too much
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 23 Feb 2012 01:06:24 +0100 |
parents | b015f3918120 |
children | a57a21aa4190 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgDataFile.java Wed Feb 22 16:54:34 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Thu Feb 23 01:06:24 2012 +0100 @@ -31,9 +31,9 @@ import java.util.Collections; import java.util.List; -import org.tmatesoft.hg.core.HgDataStreamException; import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.HgInvalidControlFileException; +import org.tmatesoft.hg.core.HgInvalidFileException; import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.HgLogCommand; import org.tmatesoft.hg.core.Nodeid; @@ -93,23 +93,27 @@ * @param nodeid revision of the file * * @return size of the file content at the given revision - * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog - * @throws HgDataStreamException if attempt to access file metadata failed + * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog (<em>runtime exception</em>) * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public int length(Nodeid nodeid) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException { - return length(getRevisionIndex(nodeid)); + public int length(Nodeid nodeid) throws HgInvalidControlFileException, HgInvalidRevisionException { + try { + return length(getRevisionIndex(nodeid)); + } catch (HgInvalidControlFileException ex) { + throw ex.isRevisionSet() ? ex : ex.setRevision(nodeid); + } catch (HgInvalidRevisionException ex) { + throw ex.isRevisionSet() ? ex : ex.setRevision(nodeid); + } } /** * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, only {@link HgRepository#TIP} makes sense. * @return size of the file content at the revision identified by local revision number. - * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog - * @throws HgDataStreamException if attempt to access file metadata failed + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public int length(int fileRevisionIndex) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException { - // TODO support WORKING_COPY constant + public int length(int fileRevisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { + // FIXME support WORKING_COPY constant if (metadata == null || !metadata.checked(fileRevisionIndex)) { checkAndRecordMetadata(fileRevisionIndex); } @@ -126,11 +130,12 @@ * as if it would be refreshed in the working copy, i.e. its corresponding revision * (XXX according to dirstate? file tip?) is read from the repository, and filters repo -> working copy get applied. * - * @param sink where to pipe content to - * @throws HgDataStreamException to indicate troubles reading repository file + * @param sink content consumer + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgInvalidFileException if access to file in working directory failed * @throws CancelledException if execution of the operation was cancelled */ - public void workingCopy(ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException { + public void workingCopy(ByteChannel sink) throws HgException, CancelledException { File f = getRepo().getFile(this); if (f.exists()) { final CancelSupport cs = CancelSupport.Factory.get(sink); @@ -150,7 +155,7 @@ buf.compact(); } } catch (IOException ex) { - throw new HgDataStreamException(getPath(), ex); + throw new HgInvalidFileException("Working copy read failed", ex, f); } finally { progress.done(); if (fc != null) { @@ -165,7 +170,8 @@ final Pair<Nodeid, Nodeid> wcParents = getRepo().getWorkingCopyParents(); Nodeid p = wcParents.first().isNull() ? wcParents.second() : wcParents.first(); if (p.isNull()) { - // no dirstate parents - no content + // no dirstate parents - no content + // XXX what if it's repository with no dirstate? Shall I use TIP then? return; } final HgChangelog clog = getRepo().getChangelog(); @@ -184,49 +190,39 @@ } } -// public void content(int revision, ByteChannel sink, boolean applyFilters) throws HgDataStreamException, IOException, CancelledException { -// byte[] content = content(revision); -// final CancelSupport cancelSupport = CancelSupport.Factory.get(sink); -// final ProgressSupport progressSupport = ProgressSupport.Factory.get(sink); -// ByteBuffer buf = ByteBuffer.allocate(512); -// int left = content.length; -// progressSupport.start(left); -// int offset = 0; -// cancelSupport.checkCancelled(); -// ByteChannel _sink = applyFilters ? new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath())) : sink; -// do { -// buf.put(content, offset, Math.min(left, buf.remaining())); -// buf.flip(); -// cancelSupport.checkCancelled(); -// // XXX I may not rely on returned number of bytes but track change in buf position instead. -// int consumed = _sink.write(buf); -// buf.compact(); -// offset += consumed; -// left -= consumed; -// progressSupport.worked(consumed); -// } while (left > 0); -// progressSupport.done(); // XXX shall specify whether #done() is invoked always or only if completed successfully. -// } - - /*XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves?*/ - public void contentWithFilters(int revision, ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException, HgInvalidRevisionException { - if (revision == WORKING_COPY) { + /** + * Access content of a file revision + * XXX not sure distinct method contentWithFilters() is the best way to do, perhaps, callers shall add filters themselves? + * + * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. + * @param sink content consumer + * + * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgInvalidFileException if access to file in working directory failed + * @throws CancelledException if execution of the operation was cancelled + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) + */ + public void contentWithFilters(int fileRevisionIndex, ByteChannel sink) throws HgException, CancelledException, HgInvalidRevisionException { + if (fileRevisionIndex == WORKING_COPY) { workingCopy(sink); // pass un-mangled sink } else { - content(revision, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath()))); + content(fileRevisionIndex, new FilterByteChannel(sink, getRepo().getFiltersFromRepoToWorkingDir(getPath()))); } } /** + * Retrieve content of specific revision. Content is provided as is, without any filters (e.g. keywords, eol, etc.) applied. + * For filtered content, use {@link #contentWithFilters(int, ByteChannel)}. * * @param fileRevisionIndex - revision local index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. - * @param sink - * @throws HgDataStreamException FIXME EXCEPTIONS + * @param sink content consumer + * * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgInvalidFileException if access to file in working directory failed * @throws CancelledException if execution of the operation was cancelled - * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog + * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog (<em>runtime exception</em>) */ - public void content(int fileRevisionIndex, ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException, HgInvalidRevisionException { + public void content(int fileRevisionIndex, ByteChannel sink) throws HgException, CancelledException, HgInvalidRevisionException { // for data files need to check heading of the file content for possible metadata // @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8- if (fileRevisionIndex == TIP) { @@ -255,19 +251,22 @@ insp = new ContentPipe(sink, metadata.dataOffset(fileRevisionIndex), lf); } else { // do not know if there's metadata - insp = new MetadataInspector(metadata, lf, getPath(), new ContentPipe(sink, 0, lf)); + insp = new MetadataInspector(metadata, lf, new ContentPipe(sink, 0, lf)); } insp.checkCancelled(); super.content.iterate(fileRevisionIndex, fileRevisionIndex, true, insp); try { insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe? - } catch (HgDataStreamException ex) { - throw ex; + } catch (HgInvalidControlFileException ex) { + ex = ex.setFileName(getPath()); + throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(fileRevisionIndex); } catch (IOException ex) { - throw new HgDataStreamException(getPath(), ex).setRevisionIndex(fileRevisionIndex); + HgInvalidControlFileException e = new HgInvalidControlFileException("Revision content access failed", ex, null); + throw content.initWithIndexFile(e).setFileName(getPath()).setRevisionIndex(fileRevisionIndex); } catch (HgException ex) { // shall not happen, unless we changed ContentPipe or its subclass - throw new HgDataStreamException(getPath(), ex.getClass().getName(), ex); + HgInvalidControlFileException e = new HgInvalidControlFileException("Revision content access failed", ex, null); + throw content.initWithIndexFile(e).setFileName(getPath()).setRevisionIndex(fileRevisionIndex); } } @@ -460,11 +459,11 @@ } /** - * - * @return - * @throws HgDataStreamException if attempt to access file metadata failed + * Tells whether this file originates from another repository file + * @return <code>true</code> if this file is a copy of another from the repository + * @throws HgInvalidControlFileException if access to revlog or file metadata failed */ - public boolean isCopy() throws HgDataStreamException { + public boolean isCopy() throws HgInvalidControlFileException { if (metadata == null || !metadata.checked(0)) { checkAndRecordMetadata(0); } @@ -478,17 +477,17 @@ * Get name of the file this one was copied from. * * @return name of the file origin - * @throws HgDataStreamException if attempt to access file metadata failed + * @throws HgInvalidControlFileException if access to revlog or file metadata failed * @throws UnsupportedOperationException if this file doesn't represent a copy ({@link #isCopy()} was false) */ - public Path getCopySourceName() throws HgDataStreamException { + public Path getCopySourceName() throws HgInvalidControlFileException { if (isCopy()) { return Path.create(metadata.find(0, "copy")); } throw new UnsupportedOperationException(); // XXX REVISIT, think over if Exception is good (clients would check isCopy() anyway, perhaps null is sufficient?) } - public Nodeid getCopySourceRevision() throws HgDataStreamException { + public Nodeid getCopySourceRevision() throws HgInvalidControlFileException { if (isCopy()) { return Nodeid.fromAscii(metadata.find(0, "copyrev")); // XXX reuse/cache Nodeid } @@ -504,7 +503,7 @@ return sb.toString(); } - private void checkAndRecordMetadata(int localRev) throws HgDataStreamException { + private void checkAndRecordMetadata(int localRev) throws HgInvalidControlFileException { // content() always initializes metadata. // FIXME this is expensive way to find out metadata, distinct RevlogStream.Iterator would be better. // Alternatively, may parameterize MetadataContentPipe to do prepare only. @@ -520,7 +519,10 @@ } catch (CancelledException ex) { // it's ok, we did that } catch (HgInvalidControlFileException ex) { - throw new HgDataStreamException(getPath(), ex); + throw ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(localRev); + } catch (HgException ex) { + // metadata comes from the content, hence initWithDataFile + throw content.initWithDataFile(new HgInvalidControlFileException(null, ex, null)); } } @@ -616,12 +618,10 @@ private static class MetadataInspector extends ErrorHandlingInspector implements RevlogStream.Inspector { private final Metadata metadata; private final RevlogStream.Inspector delegate; - private final Path fname; // need these only for error reporting private final LogFacility log; - public MetadataInspector(Metadata _metadata, LogFacility logFacility, Path file, RevlogStream.Inspector chain) { + public MetadataInspector(Metadata _metadata, LogFacility logFacility, RevlogStream.Inspector chain) { metadata = _metadata; - fname = file; log = logFacility; delegate = chain; setCancelSupport(CancelSupport.Factory.get(chain)); @@ -648,12 +648,12 @@ } } catch (IOException ex) { recordFailure(ex); - } catch (HgDataStreamException ex) { - recordFailure(ex.setRevisionIndex(revisionNumber)); + } catch (HgInvalidControlFileException ex) { + recordFailure(ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(revisionNumber)); } } - private int parseMetadata(DataAccess data, final int daLength, ArrayList<MetadataEntry> _metadata) throws IOException, HgDataStreamException { + private int parseMetadata(DataAccess data, final int daLength, ArrayList<MetadataEntry> _metadata) throws IOException, HgInvalidControlFileException { int lastEntryStart = 2; int lastColon = -1; // XXX in fact, need smth like ByteArrayBuilder, similar to StringBuilder, @@ -705,7 +705,7 @@ // data.isEmpty is not reliable, renamed files of size==0 keep only metadata if (!metadataIsComplete) { // XXX perhaps, worth a testcase (empty file, renamed, read or ask ifCopy - throw new HgDataStreamException(fname, "Metadata is not closed properly", null); + throw new HgInvalidControlFileException("Metadata is not closed properly", null, null); } return lastEntryStart; }