# HG changeset patch # User Artem Tikhomirov # Date 1332539478 -3600 # Node ID 9c9c442b5f2ec67e2e5da4161338600daef34ada # Parent 5d1cc7366d04959b1d5dfc0528381827bad875a9 Major refactoring of exception handling. Low-level API uses RuntimeExceptions, while checked are left for higher level diff -r 5d1cc7366d04 -r 9c9c442b5f2e cmdline/org/tmatesoft/hg/console/Bundle.java --- a/cmdline/org/tmatesoft/hg/console/Bundle.java Fri Mar 23 21:26:01 2012 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Bundle.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.LinkedList; -import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgBundle; @@ -62,16 +61,12 @@ private final HgChangelog changelog = hgRepo.getChangelog(); public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { - try { - if (changelog.isKnown(nodeid)) { - System.out.print("+"); - } else { - System.out.print("-"); - } - System.out.printf("%d:%s\n%s\n", revisionNumber, nodeid.shortNotation(), cset.toString()); - } catch (HgException ex) { - throw new HgCallbackTargetException.Wrap(ex); + if (changelog.isKnown(nodeid)) { + System.out.print("+"); + } else { + System.out.print("-"); } + System.out.printf("%d:%s\n%s\n", revisionNumber, nodeid.shortNotation(), cset.toString()); } }); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java --- a/cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java Fri Mar 23 21:26:01 2012 +0100 +++ b/cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -93,14 +93,14 @@ StringBuilder sb = new StringBuilder(); Formatter f = new Formatter(sb); final Nodeid csetNodeid = cset.getNodeid(); - f.format("changeset: %d:%s\n", cset.getRevision(), complete ? csetNodeid : csetNodeid.shortNotation()); - if (cset.getRevision() == tip || repo.getTags().isTagged(csetNodeid)) { + f.format("changeset: %d:%s\n", cset.getRevisionIndex(), complete ? csetNodeid : csetNodeid.shortNotation()); + if (cset.getRevisionIndex() == tip || repo.getTags().isTagged(csetNodeid)) { sb.append("tag: "); for (String t : repo.getTags().tags(csetNodeid)) { sb.append(t); sb.append(' '); } - if (cset.getRevision() == tip) { + if (cset.getRevisionIndex() == tip) { sb.append("tip"); } sb.append('\n'); diff -r 5d1cc7366d04 -r 9c9c442b5f2e cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Fri Mar 23 21:26:01 2012 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Fri Mar 23 22:51:18 2012 +0100 @@ -28,7 +28,6 @@ import java.util.Map; import org.junit.Assert; -import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgCatCommand; import org.tmatesoft.hg.core.HgChangeset; @@ -57,6 +56,7 @@ import org.tmatesoft.hg.repo.HgManifest.Flags; import org.tmatesoft.hg.repo.HgMergeState; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.repo.HgStatusInspector; import org.tmatesoft.hg.repo.HgSubrepoLocation; @@ -131,42 +131,38 @@ cmd.file("file1", false); cmd.execute(new HgChangesetTreeHandler() { public void next(HgChangesetTreeHandler.TreeElement entry) { - try { - StringBuilder sb = new StringBuilder(); - HashSet test = new HashSet(entry.childRevisions()); - for (HgChangeset cc : entry.children()) { - sb.append(cc.getRevisionIndex()); - sb.append(':'); - sb.append(cc.getNodeid().shortNotation()); - sb.append(", "); - } - final Pair parents = entry.parentRevisions(); - final boolean isJoin = !parents.first().isNull() && !parents.second().isNull(); - final boolean isFork = entry.children().size() > 1; - final HgChangeset cset = entry.changeset(); - System.out.printf("%d:%s - %s\n", cset.getRevisionIndex(), cset.getNodeid().shortNotation(), cset.getComment()); - if (!isJoin && !isFork && !entry.children().isEmpty()) { - System.out.printf("\t=> %s\n", sb); + StringBuilder sb = new StringBuilder(); + HashSet test = new HashSet(entry.childRevisions()); + for (HgChangeset cc : entry.children()) { + sb.append(cc.getRevisionIndex()); + sb.append(':'); + sb.append(cc.getNodeid().shortNotation()); + sb.append(", "); + } + final Pair parents = entry.parentRevisions(); + final boolean isJoin = !parents.first().isNull() && !parents.second().isNull(); + final boolean isFork = entry.children().size() > 1; + final HgChangeset cset = entry.changeset(); + System.out.printf("%d:%s - %s\n", cset.getRevisionIndex(), cset.getNodeid().shortNotation(), cset.getComment()); + if (!isJoin && !isFork && !entry.children().isEmpty()) { + System.out.printf("\t=> %s\n", sb); + } + if (isJoin) { + HgChangeset p1 = entry.parents().first(); + HgChangeset p2 = entry.parents().second(); + System.out.printf("\tjoin <= (%d:%s, %d:%s)", p1.getRevisionIndex(), p1.getNodeid().shortNotation(), p2.getRevisionIndex(), p2.getNodeid().shortNotation()); + if (isFork) { + System.out.print(", "); } - if (isJoin) { - HgChangeset p1 = entry.parents().first(); - HgChangeset p2 = entry.parents().second(); - System.out.printf("\tjoin <= (%d:%s, %d:%s)", p1.getRevisionIndex(), p1.getNodeid().shortNotation(), p2.getRevisionIndex(), p2.getNodeid().shortNotation()); - if (isFork) { - System.out.print(", "); - } + } + if (isFork) { + if (!isJoin) { + System.out.print('\t'); } - if (isFork) { - if (!isJoin) { - System.out.print('\t'); - } - System.out.printf("fork => [%s]", sb); - } - if (isJoin || isFork) { - System.out.println(); - } - } catch (HgException ex) { - ex.printStackTrace(); + System.out.printf("fork => [%s]", sb); + } + if (isJoin || isFork) { + System.out.println(); } } }); @@ -197,7 +193,7 @@ System.out.print("]"); } fileLocalRevisions++; - } catch (HgException ex) { + } catch (HgRuntimeException ex) { ex.printStackTrace(); } } @@ -386,7 +382,7 @@ return String.format("%s %s (%d bytes)", r.getPath(), r.getRevision(), sink.toArray().length); } - private void testFileStatus() throws HgException, IOException { + private void testFileStatus() throws Exception { // final Path path = Path.create("src/org/tmatesoft/hg/util/"); // final Path path = Path.create("src/org/tmatesoft/hg/internal/Experimental.java"); // final Path path = Path.create("missing-dir/"); @@ -506,7 +502,7 @@ } public boolean next(Nodeid nid, String fname, String flags) { - throw new HgBadStateException(HgManifest.Inspector2.class.getName()); + throw new IllegalStateException(HgManifest.Inspector2.class.getName()); } public boolean next(Nodeid nid, Path fname, Flags flags) { System.out.println(nid + "\t" + fname + "\t\t" + flags); @@ -529,15 +525,11 @@ System.out.println(p); } public void file(HgFileRevision fileRevision) { - try { - System.out.print(fileRevision.getRevision());; - System.out.print(" "); - System.out.printf("%s %s", fileRevision.getParents().first().shortNotation(), fileRevision.getParents().second().shortNotation()); - System.out.print(" "); - System.out.println(fileRevision.getPath()); - } catch (HgException ex) { - throw new HgCallbackTargetException.Wrap(ex); - } + System.out.print(fileRevision.getRevision());; + System.out.print(" "); + System.out.printf("%s %s", fileRevision.getParents().first().shortNotation(), fileRevision.getParents().second().shortNotation()); + System.out.print(" "); + System.out.println(fileRevision.getPath()); } public void end(Nodeid manifestRevision) { diff -r 5d1cc7366d04 -r 9c9c442b5f2e cmdline/org/tmatesoft/hg/console/Manifest.java --- a/cmdline/org/tmatesoft/hg/console/Manifest.java Fri Mar 23 21:26:01 2012 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Manifest.java Fri Mar 23 22:51:18 2012 +0100 @@ -20,10 +20,10 @@ import static org.tmatesoft.hg.repo.HgRepository.TIP; import org.tmatesoft.hg.core.HgFileRevision; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.HgManifestCommand; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidRevisionException; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Path; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/ChangesetTransformer.java --- a/src/org/tmatesoft/hg/core/ChangesetTransformer.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/ChangesetTransformer.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,6 +22,7 @@ import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; +import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.PathPool; @@ -35,7 +36,7 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ -/*package-local*/ class ChangesetTransformer implements HgChangelog.Inspector { +/*package-local*/ class ChangesetTransformer implements HgChangelog.Inspector, Adaptable, CancelSupport { private final HgChangesetHandler handler; private final ProgressSupport progressHelper; private final CancelSupport cancelHelper; @@ -56,14 +57,14 @@ HgStatusCollector statusCollector = new HgStatusCollector(hgRepo); t = new Transformation(statusCollector, pw); handler = delegate; + // we let HgChangelog#range deal with progress (pipe through getAdapter) + // but use own cancellation (which involves CallbackTargetException as well, and preserves original cancellation + // exception in case clients care) cancelHelper = cs; progressHelper = ps; } public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { - if (failure != null || cancellation != null) { - return; // FIXME need a better way to stop iterating revlog - } if (branches != null && !branches.contains(cset.branch())) { return; } @@ -71,10 +72,9 @@ HgChangeset changeset = t.handle(revisionNumber, nodeid, cset); try { handler.next(changeset); - progressHelper.worked(1); cancelHelper.checkCancelled(); - } catch (RuntimeException ex) { - failure = new HgCallbackTargetException(ex).setRevision(nodeid).setRevisionIndex(revisionNumber); + } catch (HgCallbackTargetException ex) { + failure = ex.setRevision(nodeid).setRevisionIndex(revisionNumber); } catch (CancelledException ex) { cancellation = ex; } @@ -118,4 +118,19 @@ return changeset; } } + + public void checkCancelled() throws CancelledException { + if (failure != null || cancellation != null) { + // stop HgChangelog.Iterator. Our exception is for the purposes of cancellation only, + // the one we have stored (this.cancellation) is for user + throw new CancelledException(); + } + } + + public T getAdapter(Class adapterClass) { + if (adapterClass == ProgressSupport.class) { + return adapterClass.cast(progressHelper); + } + return null; + } } \ No newline at end of file diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgBadStateException.java --- a/src/org/tmatesoft/hg/core/HgBadStateException.java Fri Mar 23 21:26:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011-2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.core; - -/** - * hg4j's own internal error or unexpected state. - * XXX unless there's anything additional, there's not too much value in this class - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -@SuppressWarnings("serial") -public class HgBadStateException extends RuntimeException { - - public HgBadStateException(String message) { - super(message); - } - - public HgBadStateException(Throwable cause) { - super(cause); - } -} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgCallbackTargetException.java --- a/src/org/tmatesoft/hg/core/HgCallbackTargetException.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgCallbackTargetException.java Fri Mar 23 22:51:18 2012 +0100 @@ -16,26 +16,31 @@ */ package org.tmatesoft.hg.core; +import org.tmatesoft.hg.internal.ExceptionInfo; import org.tmatesoft.hg.util.Path; /** - * Checked exception that indicates errors in client code and tries to supply extra information about the context it occurred in. + * Checked exception that client supplied callback code can use to indicates its own errors. * - * Generally, client need to pass own error information/exceptions from within implementations of the callback methods they supply. + *

Generally, client need to pass own error information/exceptions from within implementations of the callback methods they supply. * However, there's no straightforward way to alter throws clause for these methods, and alternatives like generic {@link Exception} or * library's own {@link HgException} are rather obscure. Suggested approach is to wrap whatever exception user code produces with - * {@link RuntimeException} subclass, {@link Wrap}. Then, unwrap and re-throw with checked {@link HgCallbackTargetException}. + * {@link HgCallbackTargetException}, the only checked exception allowed out from a callback. * - * FIXME REVISIT perhaps, shall just throw HgCallbackTargetException from any handler, and do not catch anything in commands at all? - * FIXME decide whether shall root at HgException ("throws HgException, HgCallbackTargetException" looks a bit odd now) + *

It's intentionally not a subclass of {@link HgException} to avoid get mixed with library own errors and be processed separately. + * + * FIXME REVISIT shall just throw HgCallbackTargetException from any handler, and do not catch anything in commands at all. * * @author Artem Tikhomirov * @author TMate Software Ltd. */ @SuppressWarnings("serial") -public class HgCallbackTargetException extends HgException { +public class HgCallbackTargetException extends Exception { + + protected final ExceptionInfo details = new ExceptionInfo(this); + /** * @param cause can't be null */ @@ -44,16 +49,11 @@ if (cause == null) { throw new IllegalArgumentException(); } - if (cause.getClass() == Wrap.class) { - // eliminate wrapper - initCause(cause.getCause()); - } else { - initCause(cause); - } + initCause(cause); } @SuppressWarnings("unchecked") - public T getTargetException() { + public T getTargetException() { return (T) getCause(); } @@ -64,39 +64,22 @@ @Override public String getMessage() { StringBuilder sb = new StringBuilder(); - sb.append("Original exception thrown: "); + sb.append("Error from callback. Original exception thrown: "); sb.append(getCause().getClass().getName()); sb.append(" at "); - extras.appendDetails(sb); + details.appendDetails(sb); return sb.toString(); } - @Override public HgCallbackTargetException setRevision(Nodeid r) { - return (HgCallbackTargetException) super.setRevision(r); - } - @Override - public HgCallbackTargetException setRevisionIndex(int rev) { - return (HgCallbackTargetException) super.setRevisionIndex(rev); - } - @Override - public HgCallbackTargetException setFileName(Path name) { - return (HgCallbackTargetException) super.setFileName(name); + return details.setRevision(r); } - /** - * Given the approach high-level handlers throw RuntimeExceptions to indicate errors, and - * a need to throw reasonable checked exception from client code, clients may utilize this class - * to get their checked exceptions unwrapped by {@link HgCallbackTargetException} and serve as that - * exception cause, eliminating {@link RuntimeException} mediator. - */ - public static final class Wrap extends RuntimeException { + public HgCallbackTargetException setRevisionIndex(int rev) { + return details.setRevisionIndex(rev); + } - public Wrap(Throwable cause) { - super(cause); - if (cause == null) { - throw new IllegalArgumentException(); - } - } + public HgCallbackTargetException setFileName(Path name) { + return details.setFileName(name); } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgCatCommand.java --- a/src/org/tmatesoft/hg/core/HgCatCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgCatCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -24,6 +24,9 @@ import java.nio.ByteBuffer; import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidFileException; +import org.tmatesoft.hg.repo.HgInvalidRevisionException; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.ByteChannel; @@ -161,6 +164,7 @@ int csetRev = repo.getChangelog().getRevisionIndex(cset); Nodeid toExtract = null; do { + // TODO post-1.0 perhaps, HgChangesetFileSneaker may come handy? toExtract = repo.getManifest().getFileRevision(csetRev, file); if (toExtract == null) { if (dataFile.isCopy()) { @@ -172,7 +176,8 @@ } } while (toExtract == null); if (toExtract == null) { - throw new HgBadStateException(String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation())); + // TODO explicit FileNotFoundException? + throw new HgBadArgumentException(String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation()), null); } revToExtract = dataFile.getRevisionIndex(toExtract); } else if (revision != null) { diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgChangeset.java --- a/src/org/tmatesoft/hg/core/HgChangeset.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangeset.java Fri Mar 23 22:51:18 2012 +0100 @@ -23,8 +23,12 @@ import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgInvalidStateException; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.repo.HgStatusCollector; +import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Path; @@ -170,10 +174,9 @@ * Figures out files and specific revisions thereof that were modified in this commit * * @return revisions of files modified in this commit - * @throws HgInvalidControlFileException if access to revlog index/data entry failed - * @throws HgException in case of some other library issue + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public List getModifiedFiles() throws HgException { + public List getModifiedFiles() throws HgRuntimeException { if (modifiedFiles == null) { initFileChanges(); } @@ -184,10 +187,9 @@ * Figures out files added in this commit * * @return revisions of files added in this commit - * @throws HgInvalidControlFileException if access to revlog index/data entry failed - * @throws HgException in case of some other library issue + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public List getAddedFiles() throws HgException { + public List getAddedFiles() throws HgRuntimeException { if (addedFiles == null) { initFileChanges(); } @@ -198,10 +200,9 @@ * Figures out files that were deleted as part of this commit * * @return revisions of files deleted in this commit - * @throws HgInvalidControlFileException if access to revlog index/data entry failed - * @throws HgException in case of some other library issue + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public List getRemovedFiles() throws HgException { + public List getRemovedFiles() throws HgRuntimeException { if (deletedFiles == null) { initFileChanges(); } @@ -215,9 +216,9 @@ /** * @return never null - * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public Nodeid getFirstParentRevision() throws HgInvalidControlFileException { + public Nodeid getFirstParentRevision() throws HgRuntimeException { if (parentHelper != null) { return parentHelper.safeFirstParent(nodeid); } @@ -232,9 +233,9 @@ /** * @return never null - * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public Nodeid getSecondParentRevision() throws HgInvalidControlFileException { + public Nodeid getSecondParentRevision() throws HgRuntimeException { if (parentHelper != null) { return parentHelper.safeSecondParent(nodeid); } @@ -260,17 +261,22 @@ } } - private /*synchronized*/ void initFileChanges() throws HgException { + private /*synchronized*/ void initFileChanges() throws HgRuntimeException { ArrayList deleted = new ArrayList(); ArrayList modified = new ArrayList(); ArrayList added = new ArrayList(); HgStatusCollector.Record r = new HgStatusCollector.Record(); - statusHelper.change(revNumber, r); + try { + statusHelper.change(revNumber, r); + } catch (CancelledException ex) { + // Record can't cancel + throw new HgInvalidStateException("Internal error"); + } final HgRepository repo = statusHelper.getRepo(); for (Path s : r.getModified()) { Nodeid nid = r.nodeidAfterChange(s); if (nid == null) { - throw new HgException(String.format("For the file %s recorded as modified couldn't find revision after change", s)); + throw new HgInvalidStateException(String.format("For the file %s recorded as modified in changeset %d couldn't find revision after change", s, revNumber)); } modified.add(new HgFileRevision(repo, nid, null, s, null)); } @@ -278,7 +284,7 @@ for (Path s : r.getAdded()) { Nodeid nid = r.nodeidAfterChange(s); if (nid == null) { - throw new HgException(String.format("For the file %s recorded as added couldn't find revision after change", s)); + throw new HgInvalidStateException(String.format("For the file %s recorded as added in changeset %d couldn't find revision after change", s, revNumber)); } added.add(new HgFileRevision(repo, nid, null, s, copied.get(s))); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java --- a/src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangesetFileSneaker.java Fri Mar 23 22:51:18 2012 +0100 @@ -18,8 +18,11 @@ import org.tmatesoft.hg.internal.ManifestRevision; import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.util.Path; import org.tmatesoft.hg.util.Status; @@ -145,7 +148,7 @@ extractRevFlags = cachedManifest.flags(file); } } - } catch (HgException ex) { + } catch (HgRuntimeException ex) { checkResult = new Status(Status.Kind.ERROR, phaseMsg, ex); return checkResult; } @@ -212,7 +215,7 @@ private void assertCheckRan() { if (checkResult == null) { - throw new HgBadStateException("Shall invoke #check(Path) first"); + throw new HgInvalidStateException("Shall invoke #check(Path) first"); } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgChangesetHandler.java --- a/src/org/tmatesoft/hg/core/HgChangesetHandler.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangesetHandler.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ */ package org.tmatesoft.hg.core; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.util.CancelledException; /** @@ -24,12 +25,12 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ +@Callback public interface HgChangesetHandler/*XXX perhaps, shall parameterize with exception clients can throw, like: */ { /** * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy. + * @throws HgCallbackTargetException wrapper for any exception user code may produce * @throws CancelledException if handler is not interested in more changesets and iteration shall stop - * @throws RuntimeException or any subclass thereof to indicate error. General contract is that RuntimeExceptions - * will be re-thrown wrapped into {@link HgCallbackTargetException}. */ - void next(HgChangeset changeset) throws CancelledException; + void next(HgChangeset changeset) throws HgCallbackTargetException, CancelledException; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java --- a/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java Fri Mar 23 22:51:18 2012 +0100 @@ -18,6 +18,7 @@ import java.util.Collection; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Pair; @@ -29,64 +30,57 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ +@Callback public interface HgChangesetTreeHandler { /** * @param entry access to various pieces of information about current tree node. Instances might be * reused across calls and shall not be kept by client's code - * @throws HgException allows implementers propagate errors from {@link TreeElement} or other parts of the library. - * @throws HgCallbackTargetException.Wrap wrapper object for any exception user code may produce. Wrapped exception would get re-thrown with {@link HgCallbackTargetException} + * @throws HgCallbackTargetException wrapper for any exception user code may produce * @throws CancelledException if execution of the operation was cancelled */ - public void next(HgChangesetTreeHandler.TreeElement entry) throws HgException, HgCallbackTargetException.Wrap, CancelledException; + public void next(HgChangesetTreeHandler.TreeElement entry) throws HgCallbackTargetException, CancelledException; interface TreeElement { /** * Revision of the revlog being iterated. For example, when walking file history, return value represents file revisions. * * @return revision of the revlog being iterated. - * @throws HgException to indicate failure dealing with Mercurial data */ - public Nodeid fileRevision() throws HgException; + public Nodeid fileRevision(); /** * @return changeset associated with the current revision - * @throws HgException to indicate failure dealing with Mercurial data */ - public HgChangeset changeset() throws HgException; + public HgChangeset changeset(); /** * Lightweight alternative to {@link #changeset()}, identifies changeset in which current file node has been modified * @return changeset {@link Nodeid revision} - * @throws HgException to indicate failure dealing with Mercurial data */ - public Nodeid changesetRevision() throws HgException; + public Nodeid changesetRevision(); /** * Node, these are not necessarily in direct relation to parents of changeset from {@link #changeset()} * @return changesets that correspond to parents of the current file node, either pair element may be null. - * @throws HgException to indicate failure dealing with Mercurial data */ - public Pair parents() throws HgException; + public Pair parents(); /** * Lightweight alternative to {@link #parents()}, give {@link Nodeid nodeids} only * @return two values, neither is null, use {@link Nodeid#isNull()} to identify parent not set - * @throws HgException to indicate failure dealing with Mercurial data */ - public Pair parentRevisions() throws HgException; + public Pair parentRevisions(); /** * Changes that originate from the given change and bear it as their parent. * @return collection (possibly empty) of immediate children of the change - * @throws HgException to indicate failure dealing with Mercurial data */ - public Collection children() throws HgException; + public Collection children(); /** * Lightweight alternative to {@link #children()}. * @return never null - * @throws HgException to indicate failure dealing with Mercurial data */ - public Collection childRevisions() throws HgException; + public Collection childRevisions(); } } \ No newline at end of file diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgCloneCommand.java --- a/src/org/tmatesoft/hg/core/HgCloneCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgCloneCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,9 +37,13 @@ import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.repo.HgBundle; import org.tmatesoft.hg.repo.HgBundle.GroupElement; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidFileException; +import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgLookup; import org.tmatesoft.hg.repo.HgRemoteRepository; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.PathRewrite; @@ -49,7 +53,7 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ -public class HgCloneCommand { +public class HgCloneCommand extends HgAbstractCommand { private File destination; private HgRemoteRepository srcRepo; @@ -72,7 +76,17 @@ return this; } - public HgRepository execute() throws HgBadArgumentException, HgRemoteConnectionException, HgInvalidFileException, CancelledException { + /** + * + * @return + * @throws HgBadArgumentException + * @throws HgRemoteConnectionException + * @throws HgRepositoryNotFoundException + * @throws HgException + * @throws CancelledException + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + */ + public HgRepository execute() throws HgException, CancelledException { if (destination == null) { throw new IllegalArgumentException("Destination not set", null); } @@ -159,7 +173,7 @@ indexFile = new FileOutputStream(new File(hgDir, filename = "store/00changelog.i")); collectChangelogIndexes = true; } catch (IOException ex) { - throw new HgBadStateException(ex); + throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename)); } } @@ -174,7 +188,7 @@ indexFile = null; filename = null; } catch (IOException ex) { - throw new HgBadStateException(ex); + throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename)); } } @@ -185,7 +199,7 @@ revisionSequence.clear(); indexFile = new FileOutputStream(new File(hgDir, filename = "store/00manifest.i")); } catch (IOException ex) { - throw new HgBadStateException(ex); + throw new HgInvalidControlFileException("Failed to write manifest", ex, new File(filename)); } } @@ -199,7 +213,7 @@ indexFile = null; filename = null; } catch (IOException ex) { - throw new HgBadStateException(ex); + throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename)); } } @@ -214,7 +228,8 @@ file.getParentFile().mkdirs(); indexFile = new FileOutputStream(file); } catch (IOException ex) { - throw new HgBadStateException(ex); + String m = String.format("Failed to write file %s", filename); + throw new HgInvalidControlFileException(m, ex, new File(filename)); } } @@ -228,7 +243,8 @@ indexFile = null; filename = null; } catch (IOException ex) { - throw new HgBadStateException(ex); + String m = String.format("Failed to write file %s", filename); + throw new HgInvalidControlFileException(m, ex, new File(filename)); } } @@ -242,7 +258,8 @@ } } } - throw new HgBadStateException(String.format("Can't find index of %s for file %s", p.shortNotation(), filename)); + String m = String.format("Can't find index of %s for file %s", p.shortNotation(), filename); + throw new HgInvalidControlFileException(m, null, null).setRevision(p); } public boolean element(GroupElement ge) { @@ -259,7 +276,8 @@ byte[] calculated = dh.sha1(p1, p2, content).asBinary(); final Nodeid node = ge.node(); if (!node.equalsTo(calculated)) { - throw new HgBadStateException(String.format("Checksum failed: expected %s, calculated %s. File %s", node, calculated, filename)); + // TODO post-1.0 custom exception ChecksumCalculationFailed? + throw new HgInvalidStateException(String.format("Checksum failed: expected %s, calculated %s. File %s", node, calculated, filename)); } final int link; if (collectChangelogIndexes) { @@ -268,7 +286,7 @@ } else { Integer csRev = changelogIndexes.get(ge.cset()); if (csRev == null) { - throw new HgBadStateException(String.format("Changelog doesn't contain revision %s of %s", ge.cset().shortNotation(), filename)); + throw new HgInvalidStateException(String.format("Changelog doesn't contain revision %s of %s", ge.cset().shortNotation(), filename)); } link = csRev.intValue(); } @@ -325,7 +343,8 @@ prevRevContent.done(); prevRevContent = new ByteArrayDataAccess(content); } catch (IOException ex) { - throw new HgBadStateException(ex); + String m = String.format("Failed to write revision %s of file %s", ge.node().shortNotation(), filename); + throw new HgInvalidControlFileException(m, ex, new File(filename)); } return true; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgDataStreamException.java --- a/src/org/tmatesoft/hg/core/HgDataStreamException.java Fri Mar 23 21:26:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011-2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.core; - -import org.tmatesoft.hg.repo.HgDataFile; -import org.tmatesoft.hg.util.Path; - -/** - * Any erroneous state with @link {@link HgDataFile} input/output, read/write operations - * - * @deprecated {@link HgInvalidControlFileException} and {@link HgInvalidFileException} deemed sufficient - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -@SuppressWarnings("serial") -@Deprecated -public class HgDataStreamException extends HgException { - - public HgDataStreamException(Path file, String message, Throwable cause) { - super(message, cause); - setFileName(file); - } - - public HgDataStreamException(Path file, Throwable cause) { - super(cause); - setFileName(file); - } - - @Override - public HgDataStreamException setRevision(Nodeid r) { - return (HgDataStreamException) super.setRevision(r); - } - - @Override - public HgDataStreamException setRevisionIndex(int rev) { - return (HgDataStreamException) super.setRevisionIndex(rev); - } - @Override - public HgDataStreamException setFileName(Path name) { - return (HgDataStreamException) super.setFileName(name); - } -} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgFileRevision.java --- a/src/org/tmatesoft/hg/core/HgFileRevision.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgFileRevision.java Fri Mar 23 22:51:18 2012 +0100 @@ -17,6 +17,8 @@ package org.tmatesoft.hg.core; import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidRevisionException; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgManifest.Flags; import org.tmatesoft.hg.repo.HgRepository; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgIncomingCommand.java --- a/src/org/tmatesoft/hg/core/HgIncomingCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgIncomingCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,10 @@ import org.tmatesoft.hg.internal.RepositoryComparator.BranchChain; import org.tmatesoft.hg.repo.HgBundle; import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidFileException; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; +import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgRemoteRepository; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.CancelledException; @@ -150,7 +153,7 @@ public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { if (parentHelper.knownNode(nodeid)) { if (!common.contains(nodeid)) { - throw new HgBadStateException("Bundle shall not report known nodes other than roots we've supplied"); + throw new HgInvalidStateException("Bundle shall not report known nodes other than roots we've supplied"); } return; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgInvalidControlFileException.java --- a/src/org/tmatesoft/hg/core/HgInvalidControlFileException.java Fri Mar 23 21:26:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2011-2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.core; - -import java.io.File; - -import org.tmatesoft.hg.internal.Experimental; -import org.tmatesoft.hg.util.Path; - -/** - * WORK IN PROGRESS - * - * Subclass of {@link HgInvalidFileException} to indicate failure to deal with one of Mercurial control files - * (most likely those under .hg/, but also those residing in the repository, with special meaning to the Mercurial, like .hgtags or .hgignore) - * - * XXX Perhaps, HgInvalidRevlogException? - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -@SuppressWarnings("serial") -@Experimental(reason="WORK IN PROGRESS. Name is likely to change") -public class HgInvalidControlFileException extends HgInvalidFileException { - - public HgInvalidControlFileException(String message, Throwable th, File file) { - super(message, th, file); - } - - @Override - public HgInvalidControlFileException setFile(File file) { - super.setFile(file); - return this; - } - - @Override - public HgInvalidControlFileException setRevision(Nodeid r) { - return (HgInvalidControlFileException) super.setRevision(r); - } - - @Override - public HgInvalidControlFileException setRevisionIndex(int rev) { - return (HgInvalidControlFileException) super.setRevisionIndex(rev); - } - - @Override - public HgInvalidControlFileException setFileName(Path name) { - return (HgInvalidControlFileException) super.setFileName(name); - } -} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgInvalidFileException.java --- a/src/org/tmatesoft/hg/core/HgInvalidFileException.java Fri Mar 23 21:26:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011-2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.core; - -import java.io.File; -import java.io.IOException; - -/** - * Thrown when there are troubles working with local file. Most likely (but not necessarily) wraps IOException. Might be - * perceived as specialized IOException with optional File and other repository information. - * - * Hg4J tries to minimize chances for IOException to occur (i.e. {@link File#canRead()} is checked before attempt to - * read a file that might not exist, and doesn't use this exception to wrap each and any {@link IOException} source (e.g. - * #close() calls are unlikely to yield it), hence it is likely to address real cases when I/O error occurs. - * - * On the other hand, when a file is supposed to exist and be readable, this exception might get thrown as well to indicate - * that's not true. - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -@SuppressWarnings("serial") -public class HgInvalidFileException extends HgException { - - public HgInvalidFileException(String message, Throwable th) { - super(message, th); - } - - public HgInvalidFileException(String message, Throwable th, File file) { - super(message, th); - extras.setFile(file); // allows null - } - - public HgInvalidFileException setFile(File file) { - assert file != null; // doesn't allow null not to clear file accidentally - extras.setFile(file); - return this; - } - - /** - * @return file object that causes troubles, or null if specific file is unknown - */ - public File getFile() { - return extras.getFile(); - } -} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgInvalidRevisionException.java --- a/src/org/tmatesoft/hg/core/HgInvalidRevisionException.java Fri Mar 23 21:26:01 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2011-2012 TMate Software Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For information on how to redistribute this software under - * the terms of a license other than GNU General Public License - * contact TMate Software at support@hg4j.com - */ -package org.tmatesoft.hg.core; - -import static org.tmatesoft.hg.repo.HgRepository.*; - -import org.tmatesoft.hg.internal.Experimental; - -/** - * Use of revision or revision local index that is not valid for a given revlog. - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -@SuppressWarnings("serial") -@Experimental(reason="1) Whether to use checked or runtime exception is not yet decided. 2) Perhaps, its use not bound to wrong arguments") -public class HgInvalidRevisionException extends IllegalArgumentException { - private Nodeid rev; - private Integer revIdx = BAD_REVISION; - // next two make sense only when revIdx is present - private int rangeLeftBoundary = BAD_REVISION, rangeRightBoundary = BAD_REVISION; - - /** - * - * this exception is not expected to be initialized with another exception, although those who need to, - * may still use {@link #initCause(Throwable)} - * @param message optional description of the issue - * @param revision invalid revision, may be null if revisionIndex is used - * @param revisionIndex invalid revision index, may be null if not known and revision is supplied - */ - public HgInvalidRevisionException(String message, Nodeid revision, Integer revisionIndex) { - super(message); - assert revision != null || revisionIndex != null; - rev = revision; - revIdx = revisionIndex; - } - - public HgInvalidRevisionException(Nodeid revision) { - this(null, revision, null); - } - - public HgInvalidRevisionException(int revisionIndex) { - this(null, null, revisionIndex); - } - - public Nodeid getRevision() { - return rev; - } - - public Integer getRevisionIndex() { - return revIdx; - } - - public HgInvalidRevisionException setRevision(Nodeid revision) { - assert revision != null; - rev = revision; - return this; - } - - // int, not Integer is on purpose, not to clear exception completely - public HgInvalidRevisionException setRevisionIndex(int revisionIndex) { - revIdx = revisionIndex; - return this; - } - - public HgInvalidRevisionException setRevisionIndex(int revisionIndex, int rangeLeft, int rangeRight) { - revIdx = revisionIndex; - rangeLeftBoundary = rangeLeft; - rangeRightBoundary = rangeRight; - return this; - } - - public boolean isRevisionSet() { - return rev != null; - } - - public boolean isRevisionIndexSet() { - return revIdx != BAD_REVISION; - } - - @Override - public String getMessage() { - String msg = super.getMessage(); - if (msg != null) { - return msg; - } - StringBuilder sb = new StringBuilder(); - if (rev != null) { - sb.append("Revision:"); - sb.append(rev.shortNotation()); - sb.append(' '); - } - if (revIdx != null) { - String sr; - switch (revIdx) { - case BAD_REVISION : sr = "UNKNOWN"; break; - case TIP : sr = "TIP"; break; - case WORKING_COPY: sr = "WORKING-COPY"; break; - case NO_REVISION : sr = "NO REVISION"; break; - default : sr = revIdx.toString(); - } - if (rangeLeftBoundary != BAD_REVISION || rangeRightBoundary != BAD_REVISION) { - sb.append(String.format("%s is not from [%d..%d]", sr, rangeLeftBoundary, rangeRightBoundary)); - } else { - sb.append(sr); - } - } - return sb.toString(); - } -} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgLogCommand.java --- a/src/org/tmatesoft/hg/core/HgLogCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgLogCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -35,7 +35,11 @@ import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidRevisionException; +import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.CancelledException; @@ -185,14 +189,22 @@ /** * Similar to {@link #execute(HgChangesetHandler)}, collects and return result as a list. + * @throws HgException FIXME EXCEPTIONS */ public List execute() throws HgException { CollectHandler collector = new CollectHandler(); try { execute(collector); + } catch (HgCallbackTargetException ex) { + // see below for CanceledException + HgInvalidStateException t = new HgInvalidStateException("Internal error"); + t.initCause(ex); + throw t; } catch (CancelledException ex) { // can't happen as long as our CollectHandler doesn't throw any exception - throw new HgBadStateException(ex); + HgInvalidStateException t = new HgInvalidStateException("Internal error"); + t.initCause(ex); + throw t; } return collector.getChanges(); } @@ -201,7 +213,7 @@ * Iterate over range of changesets configured in the command. * * @param handler callback to process changesets. - * @throws HgCallbackTargetException to re-throw exception from the handler + * @throws HgCallbackTargetException wrapper for any exception user callback code may produce * @throws HgInvalidControlFileException if access to revlog index/data entry failed * @throws HgException in case of some other library issue * @throws CancelledException if execution of the command was cancelled @@ -237,11 +249,7 @@ if (handler instanceof FileHistoryHandler) { HgFileRevision src = new HgFileRevision(repo, fileNode.getCopySourceRevision(), null, fileNode.getCopySourceName()); HgFileRevision dst = new HgFileRevision(repo, fileNode.getRevision(0), null, fileNode.getPath(), src.getPath()); - try { - ((FileHistoryHandler) handler).copy(src, dst); - } catch (HgCallbackTargetException.Wrap ex) { - throw new HgCallbackTargetException(ex).setRevision(fileNode.getCopySourceRevision()).setFileName(fileNode.getCopySourceName()); - } + ((FileHistoryHandler) handler).copy(src, dst); } if (limit > 0 && count >= limit) { // if limit reach, follow is useless. @@ -331,17 +339,13 @@ } else { ph2 = new ProgressSupport.Sub(progressHelper, 3); } - try { - ph2.start(completeHistory.length); - // XXX shall sort completeHistory according to changeset numbers? - for (int i = 0; i < completeHistory.length; i++ ) { - final HistoryNode n = completeHistory[i]; - handler.next(ei.init(n)); - ph2.worked(1); - cancelHelper.checkCancelled(); - } - } catch (HgCallbackTargetException.Wrap ex) { - throw new HgCallbackTargetException(ex); + ph2.start(completeHistory.length); + // XXX shall sort completeHistory according to changeset numbers? + for (int i = 0; i < completeHistory.length; i++ ) { + final HistoryNode n = completeHistory[i]; + handler.next(ei.init(n)); + ph2.worked(1); + cancelHelper.checkCancelled(); } progressHelper.done(); } @@ -399,9 +403,9 @@ public interface FileHistoryHandler extends HgChangesetHandler { // FIXME move to stanalone class file, perhaps? // XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them? /** - * @throws HgCallbackTargetException.Wrap wrapper object for any exception user code may produce. Wrapped exception would get re-thrown with {@link HgCallbackTargetException} + * @throws HgCallbackTargetException wrapper object for any exception user code may produce */ - void copy(HgFileRevision from, HgFileRevision to) throws HgCallbackTargetException.Wrap; + void copy(HgFileRevision from, HgFileRevision to) throws HgCallbackTargetException; } public static class CollectHandler implements HgChangesetHandler { @@ -471,11 +475,11 @@ return historyNode.fileRevision; } - public HgChangeset changeset() throws HgException { + public HgChangeset changeset() { return get(historyNode.changeset)[0]; } - public Pair parents() throws HgException { + public Pair parents() { if (parents != null) { return parents; } @@ -495,7 +499,7 @@ return parents = new Pair(r[0], r[1]); } - public Collection children() throws HgException { + public Collection children() { if (children != null) { return children; } @@ -516,7 +520,7 @@ cachedChangesets.put(cs.getRevisionIndex(), cs); } - private HgChangeset[] get(int... changelogRevisionIndex) throws HgException { + private HgChangeset[] get(int... changelogRevisionIndex) { HgChangeset[] rv = new HgChangeset[changelogRevisionIndex.length]; IntVector misses = new IntVector(changelogRevisionIndex.length, -1); for (int i = 0; i < changelogRevisionIndex.length; i++) { @@ -538,7 +542,8 @@ for (int changeset2read : changesets2read) { HgChangeset cs = cachedChangesets.get(changeset2read); if (cs == null) { - throw new HgException(String.format("Can't get changeset for revision %d", changeset2read)); + HgInvalidStateException t = new HgInvalidStateException(String.format("Can't get changeset for revision %d", changeset2read)); + throw t.setRevisionIndex(changeset2read); } // HgChangelog.range may reorder changesets according to their order in the changelog // thus need to find original index @@ -560,7 +565,7 @@ } // init only when needed - void initTransform() throws HgInvalidControlFileException { + void initTransform() throws HgRuntimeException { if (transform == null) { transform = new ChangesetTransformer.Transformation(new HgStatusCollector(repo)/*XXX try to reuse from context?*/, getParentHelper(false)); } @@ -571,14 +576,14 @@ populate(cs.clone()); } - public Nodeid changesetRevision() throws HgException { + public Nodeid changesetRevision() { if (changesetRevision == null) { changesetRevision = getRevision(historyNode.changeset); } return changesetRevision; } - public Pair parentRevisions() throws HgException { + public Pair parentRevisions() { if (parentRevisions == null) { HistoryNode p; final Nodeid p1, p2; @@ -597,7 +602,7 @@ return parentRevisions; } - public Collection childRevisions() throws HgException { + public Collection childRevisions() { if (childRevisions != null) { return childRevisions; } @@ -614,8 +619,8 @@ } // reading nodeid involves reading index only, guess, can afford not to optimize multiple reads - private Nodeid getRevision(int changelogRevisionNumber) throws HgInvalidControlFileException { - // TODO [post-1.0] pipe through pool + private Nodeid getRevision(int changelogRevisionNumber) { + // TODO post-1.0 pipe through pool HgChangeset cs = cachedChangesets.get(changelogRevisionNumber); if (cs != null) { return cs.getNodeid(); diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgManifestCommand.java --- a/src/org/tmatesoft/hg/core/HgManifestCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgManifestCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ import java.util.LinkedList; import java.util.List; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgManifest.Flags; @@ -121,7 +122,8 @@ /** * Callback to walk file/directory tree of a revision */ - public interface Handler { + @Callback + public interface Handler { // FIXME TLC void begin(Nodeid manifestRevision); void dir(Path p); // optionally invoked (if walker was configured to spit out directories) prior to any files from this dir and subdirs void file(HgFileRevision fileRevision); // XXX allow to check p is invalid (df.exists()) @@ -180,7 +182,7 @@ return true; } public boolean next(Nodeid nid, String fname, String flags) { - throw new HgBadStateException(HgManifest.Inspector2.class.getName()); + throw new IllegalStateException(HgManifest.Inspector2.class.getName()); } public boolean next(Nodeid nid, Path fname, Flags flags) { diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgOutgoingCommand.java --- a/src/org/tmatesoft/hg/core/HgOutgoingCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgOutgoingCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,6 +22,7 @@ import org.tmatesoft.hg.internal.RepositoryComparator; import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgRemoteRepository; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.CancelSupport; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgRepoFacade.java --- a/src/org/tmatesoft/hg/core/HgRepoFacade.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgRepoFacade.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,10 +66,11 @@ /** * Tries to find repository starting from the current working directory. + * * @return true if found valid repository - * @throws HgInvalidFileException in case of errors during repository initialization + * @throws HgRepositoryNotFoundException if no repository found in working directory */ - public boolean init() throws HgInvalidFileException { + public boolean init() throws HgRepositoryNotFoundException { repo = new HgLookup(context).detectFromWorkingDir(); return repo != null && !repo.isInvalid(); } @@ -79,10 +80,10 @@ * * @param repoLocation path to any folder within structure of a Mercurial repository. * @return true if found valid repository - * @throws HgInvalidFileException if there are errors accessing specified location + * @throws HgRepositoryNotFoundException if there's no repository at specified location * @throws IllegalArgumentException if argument is null */ - public boolean initFrom(File repoLocation) throws HgInvalidFileException { + public boolean initFrom(File repoLocation) throws HgRepositoryNotFoundException { if (repoLocation == null) { throw new IllegalArgumentException(); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgRepositoryNotFoundException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgRepositoryNotFoundException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.core; + +/** + * Indicates failure to find repository at specified location + * XXX may provide information about alternatives tried + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +public class HgRepositoryNotFoundException extends HgException { + + private String location; + + public HgRepositoryNotFoundException(String message) { + super(message); + } + + public HgRepositoryNotFoundException setLocation(String location) { + this.location = location; + return this; + } + + public String getLocation() { + return this.location; + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgStatus.java --- a/src/org/tmatesoft/hg/core/HgStatus.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatus.java Fri Mar 23 22:51:18 2012 +0100 @@ -20,6 +20,7 @@ import java.util.Date; import org.tmatesoft.hg.internal.ChangelogHelper; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.util.Path; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgStatusCommand.java --- a/src/org/tmatesoft/hg/core/HgStatusCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatusCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,13 +22,14 @@ import java.io.IOException; import java.util.ConcurrentModificationException; -import java.util.concurrent.CancellationException; import org.tmatesoft.hg.internal.ChangelogHelper; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.repo.HgStatusInspector; import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; +import org.tmatesoft.hg.util.CancelSupport; +import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Path; import org.tmatesoft.hg.util.Status; @@ -161,11 +162,13 @@ * Perform status operation according to parameters set. * * @param statusHandler callback to get status information - * @throws IOException if there are (further unspecified) errors while walking working copy + * @throws HgCallbackTargetException wrapper for any exception user callback code may produce + * @throws CancelledException if execution of the command was cancelled + * @throws IOException FIXME EXCEPTIONS WTF it's doing here if there are (further unspecified) errors while walking working copy * @throws IllegalArgumentException if handler is null * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread) */ - public void execute(HgStatusHandler statusHandler) throws CancellationException, HgException, IOException { + public void execute(HgStatusHandler statusHandler) throws HgCallbackTargetException, CancelledException, HgException, IOException { if (statusHandler == null) { throw new IllegalArgumentException(); } @@ -177,7 +180,7 @@ try { // XXX if I need a rough estimation (for ProgressMonitor) of number of work units, // I may use number of files in either rev1 or rev2 manifest edition - mediator.start(statusHandler, new ChangelogHelper(repo, startRevision)); + mediator.start(statusHandler, getCancelSupport(statusHandler, true), new ChangelogHelper(repo, startRevision)); if (endRevision == WORKING_COPY) { HgWorkingCopyStatusCollector wcsc = scope != null ? HgWorkingCopyStatusCollector.create(repo, scope) : new HgWorkingCopyStatusCollector(repo); wcsc.setBaseRevisionCollector(sc); @@ -190,11 +193,10 @@ sc.walk(startRevision, endRevision, mediator); } } - } catch (HgCallbackTargetException.Wrap ex) { - // seems too general to catch RuntimeException, i.e. - // unless catch is for very narrow piece of code, it's better not to catch any RTE (which may happen elsewhere, not only in handler) - // XXX Perhaps, need more detailed explanation in handlers that are expected to throw Wrap/RTE (i.e. HgChangesetHandler) - throw new HgCallbackTargetException(ex).setRevisionIndex(endRevision); + } catch (CancelledException ex) { + // this is our exception, thrown from Mediator. + // next check shall throw original cause of the stop - either HgCallbackTargetException or original CancelledException + mediator.checkFailure(); } finally { mediator.done(); } @@ -208,7 +210,7 @@ void handleStatus(HgStatus s); } - private class Mediator implements HgStatusInspector { + private class Mediator implements HgStatusInspector, CancelSupport { boolean needModified; boolean needAdded; boolean needRemoved; @@ -219,68 +221,117 @@ boolean needCopies; HgStatusHandler handler; private ChangelogHelper logHelper; + private CancelSupport handlerCancelSupport; + private HgCallbackTargetException failure; + private CancelledException cancellation; Mediator() { } - public void start(HgStatusHandler h, ChangelogHelper changelogHelper) { + public void start(HgStatusHandler h, CancelSupport hcs, ChangelogHelper changelogHelper) { handler = h; + handlerCancelSupport = hcs; logHelper = changelogHelper; } public void done() { handler = null; + handlerCancelSupport = null; logHelper = null; + failure = null; + cancellation = null; } public boolean busy() { return handler != null; } + // XXX copy from ChangesetTransformer. Perhaps, can share the code? + public void checkFailure() throws HgCallbackTargetException, CancelledException { + // do not forget to clear exceptions for reuse of this instance + if (failure != null) { + HgCallbackTargetException toThrow = failure; + failure = null; + throw toThrow; + } + if (cancellation != null) { + CancelledException toThrow = cancellation; + cancellation = null; + throw toThrow; + } + } + + // XXX copy from ChangesetTransformer. code sharing note above applies + public void checkCancelled() throws CancelledException { + if (failure != null || cancellation != null) { + // stop status iteration. Our exception is for the purposes of cancellation only, + // the one we have stored (this.cancellation) is for user + throw new CancelledException(); + } + } + + private void dispatch(HgStatus s) { + try { + handler.handleStatus(s); + handlerCancelSupport.checkCancelled(); + } catch (HgCallbackTargetException ex) { + failure = ex; + } catch (CancelledException ex) { + cancellation = ex; + } + } + public void modified(Path fname) { if (needModified) { - handler.handleStatus(new HgStatus(Modified, fname, logHelper)); + dispatch(new HgStatus(Modified, fname, logHelper)); } } public void added(Path fname) { if (needAdded) { - handler.handleStatus(new HgStatus(Added, fname, logHelper)); + dispatch(new HgStatus(Added, fname, logHelper)); } } public void removed(Path fname) { if (needRemoved) { - handler.handleStatus(new HgStatus(Removed, fname, logHelper)); + dispatch(new HgStatus(Removed, fname, logHelper)); } } public void copied(Path fnameOrigin, Path fnameAdded) { if (needCopies) { // TODO post-1.0 in fact, merged files may report 'copied from' as well, correct status kind thus may differ from Added - handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); + dispatch(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); } } public void missing(Path fname) { if (needMissing) { - handler.handleStatus(new HgStatus(Missing, fname, logHelper)); + dispatch(new HgStatus(Missing, fname, logHelper)); } } public void unknown(Path fname) { if (needUnknown) { - handler.handleStatus(new HgStatus(Unknown, fname, logHelper)); + dispatch(new HgStatus(Unknown, fname, logHelper)); } } public void clean(Path fname) { if (needClean) { - handler.handleStatus(new HgStatus(Clean, fname, logHelper)); + dispatch(new HgStatus(Clean, fname, logHelper)); } } public void ignored(Path fname) { if (needIgnored) { - handler.handleStatus(new HgStatus(Ignored, fname, logHelper)); + dispatch(new HgStatus(Ignored, fname, logHelper)); } } - public void invalid(Path fname, Exception ex) { - handler.handleError(fname, new Status(Status.Kind.ERROR, "Failed to get file status", ex)); + public void invalid(Path fname, Exception err) { + try { + handler.handleError(fname, new Status(Status.Kind.ERROR, "Failed to get file status", err)); + handlerCancelSupport.checkCancelled(); + } catch (HgCallbackTargetException ex) { + failure = ex; + } catch (CancelledException ex) { + cancellation = ex; + } } } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgStatusHandler.java --- a/src/org/tmatesoft/hg/core/HgStatusHandler.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatusHandler.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ */ package org.tmatesoft.hg.core; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.util.Path; import org.tmatesoft.hg.util.Status; @@ -24,19 +25,21 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ +@Callback public interface HgStatusHandler { - /* XXX #next() as in HgChangesetHandler? - * perhaps, handle() is better name? If yes, rename method in HgChangesetHandler, too, to make them similar. + /** #next() as in HgChangesetHandler? + * FIXME perhaps, handle() is better name? If yes, rename method in HgChangesetHandler, too, to make them similar. * void next(HgStatus s); - * XXX describe RTE and HgCallbackTargetException + * @throws HgCallbackTargetException wrapper for any exception user code may produce */ - void handleStatus(HgStatus s); + void handleStatus(HgStatus s) throws HgCallbackTargetException; /** * Report non-critical error processing single file during status operation * @param file name of the file that caused the trouble * @param s error description object + * @throws HgCallbackTargetException wrapper for any exception user code may produce */ - void handleError(Path file, Status s); + void handleError(Path file, Status s) throws HgCallbackTargetException; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java --- a/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java Fri Mar 23 22:51:18 2012 +0100 @@ -27,6 +27,7 @@ import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidFileException; import org.tmatesoft.hg.repo.HgRepository; /** diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/Callback.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/Callback.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.internal; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.tmatesoft.hg.core.HgCallbackTargetException; + +/** + * Marker to ease location of callback interfaces in the API. + * + * All classes/interfaces supposed to be subclassed/implemented by users, with methods throwing {@link HgCallbackTargetException} shall bear the mark. + * Besides, classes that are low-level callbacks (from {@link org.tmatesoft.hg.repo}) shall bear it, too. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ ElementType.TYPE }) +public @interface Callback { +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/ChangelogHelper.java --- a/src/org/tmatesoft/hg/internal/ChangelogHelper.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/ChangelogHelper.java Fri Mar 23 22:51:18 2012 +0100 @@ -16,10 +16,10 @@ */ package org.tmatesoft.hg.internal; -import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Path; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/DigestHelper.java --- a/src/org/tmatesoft/hg/internal/DigestHelper.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/DigestHelper.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2011 TMate Software Ltd + * Copyright (c) 2010-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,8 +21,8 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgInvalidStateException; /** @@ -50,7 +50,9 @@ sha1 = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException ex) { // could hardly happen, JDK from Sun always has sha1. - throw new HgBadStateException(ex); + HgInvalidStateException t = new HgInvalidStateException("Need SHA-1 algorithm for nodeid calculation"); + t.initCause(ex); + throw t; } } return sha1; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/ExceptionInfo.java --- a/src/org/tmatesoft/hg/internal/ExceptionInfo.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/ExceptionInfo.java Fri Mar 23 22:51:18 2012 +0100 @@ -17,6 +17,9 @@ package org.tmatesoft.hg.internal; import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; +import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; +import static org.tmatesoft.hg.repo.HgRepository.TIP; +import static org.tmatesoft.hg.repo.HgRepository.WORKING_COPY; import java.io.File; @@ -33,10 +36,12 @@ */ public class ExceptionInfo { protected final T owner; - protected int revNumber = BAD_REVISION; + protected Integer revNumber = null; protected Nodeid revision; protected Path filename; protected File localFile; + // next two make sense only when revNumber was set + private int rangeLeftBoundary = BAD_REVISION, rangeRightBoundary = BAD_REVISION; /** * @param owner instance to return from setters @@ -49,7 +54,7 @@ * @return not {@link HgRepository#BAD_REVISION} only when revision index was supplied at the construction time */ public int getRevisionIndex() { - return revNumber; + return revNumber == null ? HgRepository.BAD_REVISION : revNumber; } public T setRevisionIndex(int rev) { @@ -58,7 +63,7 @@ } public boolean isRevisionIndexSet() { - return revNumber != BAD_REVISION; + return revNumber != null; } /** @@ -100,6 +105,13 @@ public File getFile() { return localFile; } + + public T setRevisionIndexBoundary(int revisionIndex, int rangeLeft, int rangeRight) { + setRevisionIndex(revisionIndex); + rangeLeftBoundary = rangeLeft; + rangeRightBoundary = rangeRight; + return owner; + } public StringBuilder appendDetails(StringBuilder sb) { if (filename != null) { @@ -110,14 +122,34 @@ sb.append(' '); } sb.append("rev:"); - if (revNumber != BAD_REVISION) { - sb.append(revNumber); - if (revision != null) { - sb.append(':'); + boolean needNodeid = true; + if (isRevisionIndexSet()) { + if (rangeLeftBoundary != BAD_REVISION || rangeRightBoundary != BAD_REVISION) { + String sr; + switch (getRevisionIndex()) { + case BAD_REVISION: + sr = "UNKNOWN"; break; + case TIP: + sr = "TIP"; break; + case WORKING_COPY: + sr = "WORKING-COPY"; break; + case NO_REVISION: + sr = "NO REVISION"; break; + default: + sr = String.valueOf(getRevisionIndex()); + } + sb.append(String.format("%s is not from [%d..%d]", sr, rangeLeftBoundary, rangeRightBoundary)); + } else { + sb.append(getRevisionIndex()); + if (isRevisionIndexSet()) { + sb.append(':'); + sb.append(getRevision().shortNotation()); + needNodeid = false; + } } } - if (revision != null) { - sb.append(revision.shortNotation()); + if (isRevisionSet() && needNodeid) { + sb.append(getRevision().shortNotation()); } if (localFile != null) { sb.append(';'); diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/InflaterDataAccess.java --- a/src/org/tmatesoft/hg/internal/InflaterDataAccess.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/InflaterDataAccess.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,9 +22,6 @@ import java.util.zip.Inflater; import java.util.zip.ZipException; -import org.tmatesoft.hg.core.HgBadStateException; - - /** * DataAccess counterpart for InflaterInputStream. * XXX is it really needed to be subclass of FilterDataAccess? @@ -99,7 +96,7 @@ c += inflater.inflate(dummy, 0, dummy.length); } } catch (DataFormatException ex) { - throw new HgBadStateException(ex); + throw new IOException(ex); } } decompressedLength = c + oldPos; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/KeywordFilter.java --- a/src/org/tmatesoft/hg/internal/KeywordFilter.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/KeywordFilter.java Fri Mar 23 22:51:18 2012 +0100 @@ -21,12 +21,11 @@ import java.util.Date; import java.util.TreeMap; -import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; @@ -263,7 +262,7 @@ // when accessing changelog, see below, #getChangeset int csetRev = repo.getFileNode(path).getChangesetRevisionIndex(HgRepository.TIP); return repo.getChangelog().getRevision(csetRev).shortNotation(); - } catch (HgException ex) { + } catch (HgRuntimeException ex) { HgInternals.getContext(repo).getLog().error(getClass(), ex, null); return Nodeid.NULL.shortNotation(); // XXX perhaps, might return anything better? Not sure how hg approaches this. } @@ -272,7 +271,7 @@ private String username() { try { return getChangeset().user(); - } catch (HgException ex) { + } catch (HgRuntimeException ex) { HgInternals.getContext(repo).getLog().error(getClass(), ex, null); return ""; } @@ -282,14 +281,14 @@ Date d; try { d = getChangeset().date(); - } catch (HgException ex) { + } catch (HgRuntimeException ex) { HgInternals.getContext(repo).getLog().error(getClass(), ex, null); d = new Date(0l); } return String.format("%tY/% getCommon() { if (common == null) { - throw new HgBadStateException("Call #compare(Object) first"); + throw new HgInvalidStateException("Call #compare(Object) first"); } return common; } @@ -120,7 +121,7 @@ return; } if (earliestRevision < 0 || earliestRevision >= changelog.getLastRevision()) { - throw new HgBadStateException(String.format("Invalid index of common known revision: %d in total of %d", earliestRevision, 1+changelog.getLastRevision())); + throw new HgInvalidStateException(String.format("Invalid index of common known revision: %d in total of %d", earliestRevision, 1+changelog.getLastRevision())); } changelog.range(earliestRevision+1, changelog.getLastRevision(), inspector); } @@ -317,7 +318,7 @@ } } while(--watchdog > 0); if (watchdog == 0) { - throw new HgBadStateException(String.format("Can't narrow down branch [%s, %s]", rb.head.shortNotation(), rb.root.shortNotation())); + throw new HgInvalidStateException(String.format("Can't narrow down branch [%s, %s]", rb.head.shortNotation(), rb.root.shortNotation())); } } if (debug) { @@ -486,15 +487,16 @@ toQuery.clear(); } if (rootIndex == -1) { - throw new HgBadStateException("Shall not happen, provided between output is correct"); // FIXME EXCEPTIONS + throw new HgInvalidStateException("Shall not happen, provided between output is correct"); // FIXME EXCEPTIONS } result[rootIndex] = branchRoot; boolean resultOk = true; LinkedList fromRootToHead = new LinkedList(); + IntVector missing = new IntVector(); for (int i = 0; i <= rootIndex; i++) { Nodeid n = result[i]; if (n == null) { - System.out.printf("ERROR: element %d wasn't found\n",i); + missing.add(i); resultOk = false; } fromRootToHead.addFirst(n); // reverse order @@ -503,7 +505,9 @@ System.out.println("Total queries:" + totalQueries); } if (!resultOk) { - throw new HgBadStateException("See console for details"); // FIXME EXCEPTIONS + assert missing.size() > 0; + // TODO post-1.0 perhaps, there's better alternative than HgInvalidStateException, e.g. HgDataFormatException? + throw new HgInvalidStateException(String.format("Missing elements with indexes: %s", Arrays.toString(missing.toArray()))); } return fromRootToHead; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/RevlogStream.java --- a/src/org/tmatesoft/hg/internal/RevlogStream.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/RevlogStream.java Fri Mar 23 22:51:18 2012 +0100 @@ -23,12 +23,11 @@ import java.io.IOException; import java.util.zip.Inflater; -import org.tmatesoft.hg.core.HgBadStateException; -import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidRevisionException; +import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgRepository; @@ -206,7 +205,7 @@ // should be possible to use TIP, ALL, or -1, -2, -n notation of Hg // ? boolean needsNodeid - public void iterate(int start, int end, boolean needData, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException /*REVISIT - too general exception*/ { + public void iterate(int start, int end, boolean needData, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { initOutline(); final int indexSize = revisionCount(); if (indexSize == 0) { @@ -229,8 +228,6 @@ throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); } catch (HgInvalidControlFileException ex) { throw ex; - } catch (HgException ex) { - throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); } finally { r.finish(); } @@ -277,10 +274,8 @@ final int c = sortedRevisions.length; throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]",c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile); } catch (HgInvalidControlFileException ex) { + // TODO post-1.0 fill HgRuntimeException with appropriate file (either index or data, depending on error source) throw ex; - } catch (HgException ex) { - final int c = sortedRevisions.length; - throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]",c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile); } finally { r.finish(); } @@ -351,7 +346,7 @@ if (o != offset) { // just in case, can't happen, ever, unless HG (or some other bad tool) produces index file // with inlined data of size greater than 2 Gb. - throw new HgBadStateException("Data too big, offset didn't fit to sizeof(int)"); + throw new HgInvalidStateException("Data too big, offset didn't fit to sizeof(int)"); } resOffsets.add(o + REVLOGV1_RECORD_SIZE * resOffsets.size()); da.skip(3*4 + 32 + compressedLen); // Check: 44 (skip) + 20 (read) = 64 (total RevlogNG record size) @@ -372,9 +367,7 @@ } } } catch (IOException ex) { - ex.printStackTrace(); // FIXME, log error is not enough - // too bad, no outline then, but don't fail with NPE - baseRevisions = new int[0]; + throw new HgInvalidControlFileException("Failed to analyze revlog index", ex, indexFile); } finally { da.done(); } @@ -428,7 +421,7 @@ // System.out.printf("applyTime:%d ms, inspectorTime: %d ms\n", applyTime, inspectorTime); // TIMING } - public boolean range(int start, int end) throws IOException, HgException { + public boolean range(int start, int end) throws IOException { byte[] nodeidBuf = new byte[20]; int i; // it (i.e. replace with i >= start) @@ -565,6 +558,6 @@ // XXX boolean retVal to indicate whether to continue? // TODO specify nodeid and data length, and reuse policy (i.e. if revlog stream doesn't reuse nodeid[] for each call) // implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment - void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data) throws HgException; + void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data); } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/SubrepoManager.java --- a/src/org/tmatesoft/hg/internal/SubrepoManager.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/SubrepoManager.java Fri Mar 23 22:51:18 2012 +0100 @@ -27,7 +27,7 @@ import java.util.List; import java.util.Map; -import org.tmatesoft.hg.core.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgSubrepoLocation; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/internal/WinToNixPathRewrite.java --- a/src/org/tmatesoft/hg/internal/WinToNixPathRewrite.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/WinToNixPathRewrite.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,7 +22,7 @@ * Translate windows path separators to Unix/POSIX-style * * @author Artem Tikhomirov - * @author Tmate Software Ltd. + * @author TMate Software Ltd. */ public final class WinToNixPathRewrite implements PathRewrite { public CharSequence rewrite(CharSequence p) { diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgBranches.java --- a/src/org/tmatesoft/hg/repo/HgBranches.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgBranches.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,9 +34,6 @@ import java.util.TreeMap; import java.util.regex.Pattern; -import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; @@ -240,10 +237,10 @@ * Writes down information about repository branches in a format Mercurial native client can understand. * Cache file gets overwritten only if it is out of date (i.e. misses some branch information) * @throws IOException if write to cache file failed - * @throws HgException subclass of {@link HgException} in case of repository access issue + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ @Experimental(reason="Usage of cache isn't supposed to be public knowledge") - public void writeCache() throws IOException, HgException { + public void writeCache() throws IOException, HgRuntimeException { if (isCacheActual) { return; } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgBundle.java --- a/src/org/tmatesoft/hg/repo/HgBundle.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgBundle.java Fri Mar 23 22:51:18 2012 +0100 @@ -19,9 +19,7 @@ import java.io.File; import java.io.IOException; -import org.tmatesoft.hg.core.HgBadStateException; -import org.tmatesoft.hg.core.HgCallbackTargetException; -import org.tmatesoft.hg.core.HgInvalidFileException; +import org.tmatesoft.hg.core.HgBadArgumentException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.SessionContext; import org.tmatesoft.hg.internal.ByteArrayChannel; @@ -36,6 +34,8 @@ import org.tmatesoft.hg.util.CancelledException; /** + * WORK IN PROGRESS + * * @see http://mercurial.selenic.com/wiki/BundleFormat * * @author Artem Tikhomirov @@ -66,7 +66,7 @@ throw HgRepository.notImplemented(); } if (signature[4] != 'U' || signature[5] != 'N') { - throw new HgBadStateException("Bad bundle signature:" + new String(signature)); + throw new HgInvalidStateException(String.format("Bad bundle signature: %s", String.valueOf(signature))); } // "...UN", fall-through } else { @@ -96,7 +96,7 @@ * @param hgRepo repository that shall possess base revision for this bundle * @param inspector callback to get each changeset found */ - public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgCallbackTargetException, HgInvalidFileException { + public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgRuntimeException { Inspector bundleInsp = new Inspector() { DigestHelper dh = new DigestHelper(); boolean emptyChangelog = true; @@ -159,7 +159,7 @@ byte[] csetContent = ge.apply(prevRevContent); dh = dh.sha1(ge.firstParent(), ge.secondParent(), csetContent); // XXX ge may give me access to byte[] content of nodeid directly, perhaps, I don't need DH to be friend of Nodeid? if (!ge.node().equalsTo(dh.asBinary())) { - throw new IllegalStateException("Integrity check failed on " + bundleFile + ", node:" + ge.node()); + throw new HgInvalidStateException(String.format("Integrity check failed on %s, node: %s", bundleFile, ge.node().shortNotation())); } ByteArrayDataAccess csetDataAccess = new ByteArrayDataAccess(csetContent); RawChangeset cs = RawChangeset.parse(csetDataAccess); @@ -168,8 +168,10 @@ prevRevContent = csetDataAccess.reset(); } catch (CancelledException ex) { return false; - } catch (Exception ex) { - throw new HgBadStateException(ex); // FIXME EXCEPTIONS + } catch (IOException ex) { + throw new HgInvalidFileException("Invalid bundle file", ex, bundleFile); // TODO post-1.0 revisit exception handling + } catch (HgBadArgumentException ex) { + throw new HgInvalidControlFileException("Invalid bundle file", ex, bundleFile); } return true; } @@ -180,11 +182,7 @@ public void fileEnd(String name) {} }; - try { - inspectChangelog(bundleInsp); - } catch (RuntimeException ex) { - throw new HgCallbackTargetException(ex); - } + inspectChangelog(bundleInsp); } // callback to minimize amount of Strings and Nodeids instantiated @@ -209,7 +207,12 @@ boolean element(GroupElement element); } - public void inspectChangelog(Inspector inspector) throws HgInvalidFileException { + /** + * @param inspector callback to visit changelog entries + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + * @throws IllegalArgumentException if inspector argument is null + */ + public void inspectChangelog(Inspector inspector) throws HgRuntimeException { if (inspector == null) { throw new IllegalArgumentException(); } @@ -226,7 +229,12 @@ } } - public void inspectManifest(Inspector inspector) throws HgInvalidFileException { + /** + * @param inspector callback to visit manifest entries + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + * @throws IllegalArgumentException if inspector argument is null + */ + public void inspectManifest(Inspector inspector) throws HgRuntimeException { if (inspector == null) { throw new IllegalArgumentException(); } @@ -247,7 +255,12 @@ } } - public void inspectFiles(Inspector inspector) throws HgInvalidFileException { + /** + * @param inspector callback to visit file entries + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + * @throws IllegalArgumentException if inspector argument is null + */ + public void inspectFiles(Inspector inspector) throws HgRuntimeException { if (inspector == null) { throw new IllegalArgumentException(); } @@ -272,7 +285,12 @@ } } - public void inspectAll(Inspector inspector) throws HgInvalidFileException { + /** + * @param inspector visit complete bundle (changelog, manifest and file entries) + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + * @throws IllegalArgumentException if inspector argument is null + */ + public void inspectAll(Inspector inspector) throws HgRuntimeException { if (inspector == null) { throw new IllegalArgumentException(); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgChangelog.java --- a/src/org/tmatesoft/hg/repo/HgChangelog.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgChangelog.java Fri Mar 23 22:51:18 2012 +0100 @@ -32,10 +32,8 @@ import java.util.TimeZone; import org.tmatesoft.hg.core.HgBadArgumentException; -import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.IterateControlMediator; import org.tmatesoft.hg.internal.Lifecycle; @@ -107,10 +105,17 @@ return range(x, x).get(0); } + @Callback public interface Inspector { - // TODO describe whether cset is new instance each time - // describe what revisionNumber is when Inspector is used with HgBundle (BAD_REVISION or bundle's local order?) - void next(int revisionNumber, Nodeid nodeid, RawChangeset cset); + /** + * Access next changeset + * TODO describe what revisionNumber is when Inspector is used with HgBundle (BAD_REVISION or bundle's local order?) + * + * @param revisionIndex index of revision being inspected, local to the inspected object + * @param nodeid revision being inspected + * @param cset changeset raw data + */ + void next(int revisionIndex, Nodeid nodeid, RawChangeset cset); } /** @@ -386,15 +391,19 @@ progressHelper = ProgressSupport.Factory.get(delegate); } - public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) throws HgException { + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { try { byte[] data = da.byteArray(); cset.init(data, 0, data.length, usersPool); // XXX there's no guarantee for Changeset.Callback that distinct instance comes each time, consider instance reuse inspector.next(revisionNumber, Nodeid.fromBinary(nodeid, 0), cset); progressHelper.worked(1); + } catch (HgBadArgumentException ex) { + // see below about better exception + throw new HgInvalidControlFileException("Failed reading changelog", ex, null).setRevisionIndex(revisionNumber); } catch (IOException ex) { - throw new HgException(ex); // XXX need better exception, perhaps smth like HgChangelogException (extends HgInvalidControlFileException) + // XXX need better exception, perhaps smth like HgChangelogException (extends HgInvalidControlFileException) + throw new HgInvalidControlFileException("Failed reading changelog", ex, null).setRevisionIndex(revisionNumber); } if (iterateControl != null) { iterateControl.checkCancelled(); diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgDataFile.java --- a/src/org/tmatesoft/hg/repo/HgDataFile.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Fri Mar 23 22:51:18 2012 +0100 @@ -32,9 +32,6 @@ import java.util.List; 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; import org.tmatesoft.hg.internal.DataAccess; @@ -714,7 +711,7 @@ setCancelSupport(CancelSupport.Factory.get(chain)); } - public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { try { final int daLength = data.length(); if (daLength < 4 || data.readByte() != 1 || data.readByte() != 10) { @@ -736,6 +733,7 @@ } catch (IOException ex) { recordFailure(ex); } catch (HgInvalidControlFileException ex) { + // TODO RevlogStream, where this RevlogStream.Inspector goes, shall set File (as it's the only one having access to it) recordFailure(ex.isRevisionIndexSet() ? ex : ex.setRevisionIndex(revisionNumber)); } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgDirstate.java --- a/src/org/tmatesoft/hg/repo/HgDirstate.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDirstate.java Fri Mar 23 22:51:18 2012 +0100 @@ -29,7 +29,6 @@ import java.util.Map; import java.util.TreeSet; -import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.EncodingHelper; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgInternals.java --- a/src/org/tmatesoft/hg/repo/HgInternals.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgInternals.java Fri Mar 23 22:51:18 2012 +0100 @@ -25,8 +25,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.SessionContext; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.Internals; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgInvalidControlFileException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgInvalidControlFileException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011-2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import java.io.File; + +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.util.Path; + +/** + * Subclass of {@link HgInvalidFileException} to indicate failure to deal with one of Mercurial control files + * (most likely those under .hg/, but also those residing in the repository, with special meaning to the Mercurial, like .hgtags or .hgignore) + * + * XXX Perhaps, HgInvalidRevlogException? and parent HgInvalidRepositoryFileException? + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +@Experimental(reason="WORK IN PROGRESS. Name is likely to change") +public class HgInvalidControlFileException extends HgInvalidFileException { + + public HgInvalidControlFileException(String message, Throwable th, File file) { + super(message, th, file); + } + + @Override + public HgInvalidControlFileException setFile(File file) { + super.setFile(file); + return this; + } + + @Override + public HgInvalidControlFileException setRevision(Nodeid r) { + return (HgInvalidControlFileException) super.setRevision(r); + } + + @Override + public HgInvalidControlFileException setRevisionIndex(int rev) { + return (HgInvalidControlFileException) super.setRevisionIndex(rev); + } + + @Override + public HgInvalidControlFileException setFileName(Path name) { + return (HgInvalidControlFileException) super.setFileName(name); + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgInvalidFileException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgInvalidFileException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011-2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import java.io.File; +import java.io.IOException; + +/** + * Thrown when there are troubles working with local file. Most likely (but not necessarily) wraps IOException. Might be + * perceived as specialized IOException with optional File and other repository information. + * + * Hg4J tries to minimize chances for IOException to occur (i.e. {@link File#canRead()} is checked before attempt to + * read a file that might not exist, and doesn't use this exception to wrap each and any {@link IOException} source (e.g. + * #close() calls are unlikely to yield it), hence it is likely to address real cases when I/O error occurs. + * + * On the other hand, when a file is supposed to exist and be readable, this exception might get thrown as well to indicate + * that's not true. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +public class HgInvalidFileException extends HgRuntimeException { + // IMPLEMENTATION NOTE: Once needed, there might be intermediate e.g. HgDataStreamException + // (between HgInvalidFileException and HgRuntimeException) to root data access exceptions + // that do not originate from local files but e.g. a connection + + public HgInvalidFileException(String message, Throwable th) { + super(message, th); + } + + public HgInvalidFileException(String message, Throwable th, File file) { + super(message, th); + details.setFile(file); // allows null + } + + public HgInvalidFileException setFile(File file) { + assert file != null; // doesn't allow null not to clear file accidentally + details.setFile(file); + return this; + } + + /** + * @return file object that causes troubles, or null if specific file is unknown + */ + public File getFile() { + return details.getFile(); + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgInvalidRevisionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgInvalidRevisionException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011-2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import org.tmatesoft.hg.core.Nodeid; + +/** + * Use of revision or revision local index that is not valid for a given revlog. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +public class HgInvalidRevisionException extends HgRuntimeException { + + /** + * + * This exception is not expected to be initialized with another exception, although those who need to, + * may still use {@link #initCause(Throwable)} + * + * @param message optional description of the issue + * @param revision invalid revision, may be null if revisionIndex is used + * @param revisionIndex invalid revision index, may be null if not known and revision is supplied + */ + public HgInvalidRevisionException(String message, Nodeid revision, Integer revisionIndex) { + super(message, null); + assert revision != null || revisionIndex != null; + if (revision != null) { + setRevision(revision); + } + if (revisionIndex != null) { + setRevisionIndex(revisionIndex); + } + } + + public HgInvalidRevisionException(Nodeid revision) { + this(null, revision, null); + } + + public HgInvalidRevisionException(int revisionIndex) { + this(null, null, revisionIndex); + } + + public HgInvalidRevisionException setRevisionIndex(int revisionIndex, int rangeLeft, int rangeRight) { + details.setRevisionIndexBoundary(revisionIndex, rangeLeft, rangeRight); + return this; + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgInvalidStateException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgInvalidStateException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + + +/** + * Thrown to indicate unexpected or otherwise inappropriate state of the library, assumptions/preconditions not met, etc. + * Unlike {@link HgInvalidFileException} and {@link HgInvalidControlFileException}, to describe error state not related to IO operations. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +public class HgInvalidStateException extends HgRuntimeException { + + public HgInvalidStateException(String message) { + super(message, null); + // no cons with Throwable as it deemed exceptional to use HgInvalidStateException to wrap another exception + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgLookup.java --- a/src/org/tmatesoft/hg/repo/HgLookup.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgLookup.java Fri Mar 23 22:51:18 2012 +0100 @@ -22,7 +22,7 @@ import java.net.URL; import org.tmatesoft.hg.core.HgBadArgumentException; -import org.tmatesoft.hg.core.HgInvalidFileException; +import org.tmatesoft.hg.core.HgRepositoryNotFoundException; import org.tmatesoft.hg.core.SessionContext; import org.tmatesoft.hg.internal.BasicSessionContext; import org.tmatesoft.hg.internal.ConfigFile; @@ -46,40 +46,41 @@ sessionContext = ctx; } - public HgRepository detectFromWorkingDir() throws HgInvalidFileException { + public HgRepository detectFromWorkingDir() throws HgRepositoryNotFoundException { return detect(System.getProperty("user.dir")); } - public HgRepository detect(String location) throws HgInvalidFileException { + public HgRepository detect(String location) throws HgRepositoryNotFoundException { return detect(new File(location)); } // look up in specified location and above - public HgRepository detect(File location) throws HgInvalidFileException { - File dir = location.getAbsoluteFile(); - File repository; - do { - repository = new File(dir, ".hg"); - if (repository.exists() && repository.isDirectory()) { - break; + public HgRepository detect(File location) throws HgRepositoryNotFoundException { + try { + File dir = location.getAbsoluteFile(); + File repository; + do { + repository = new File(dir, ".hg"); + if (repository.exists() && repository.isDirectory()) { + break; + } + repository = null; + dir = dir.getParentFile(); + + } while(dir != null); + if (repository == null) { + throw new HgRepositoryNotFoundException(String.format("Can't locate .hg/ directory of Mercurial repository in %s nor in parent dirs", location)).setLocation(location.getPath()); } - repository = null; - dir = dir.getParentFile(); - - } while(dir != null); - if (repository == null) { - // return invalid repository - return new HgRepository(location.getPath()); - } - try { String repoPath = repository.getParentFile().getCanonicalPath(); return new HgRepository(getContext(), repoPath, repository); } catch (IOException ex) { - throw new HgInvalidFileException(location.toString(), ex, location); + HgRepositoryNotFoundException t = new HgRepositoryNotFoundException("Failed to access repository"); + t.setLocation(location.getPath()).initCause(ex); + throw t; } } - public HgBundle loadBundle(File location) throws HgInvalidFileException { + public HgBundle loadBundle(File location) throws HgRuntimeException/*FIXME need checked exception for can't find*/ { if (location == null || !location.canRead()) { throw new HgInvalidFileException(String.format("Can't read file %s", location == null ? null : location.getPath()), null, location); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgManifest.java --- a/src/org/tmatesoft/hg/repo/HgManifest.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Fri Mar 23 22:51:18 2012 +0100 @@ -17,8 +17,7 @@ package org.tmatesoft.hg.repo; import static org.tmatesoft.hg.core.Nodeid.NULL; -import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; -import static org.tmatesoft.hg.repo.HgRepository.TIP; +import static org.tmatesoft.hg.repo.HgRepository.*; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -27,12 +26,9 @@ import java.util.HashMap; import java.util.Map; -import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgChangesetFileSneaker; -import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.DigestHelper; import org.tmatesoft.hg.internal.EncodingHelper; @@ -137,8 +133,10 @@ * incrementally, nor it mandates presence of manifest version for a changeset. Thus, there might be changesets that record {@link Nodeid#NULL} * as corresponding manifest revision. This situation is deemed exceptional now and what would inspector get depends on whether * start or end arguments point to such changeset, or such changeset happen to be somewhere inside the range - * [start..end]. Implementation does it best to report empty manifests (Inspector.begin(BAD_REVISION, NULL, csetRevIndex); - * followed immediately by Inspector.end(BAD_REVISION) when start and/or end point to changeset with no associated + * [start..end]. Implementation does it best to report empty manifests + * (Inspector.begin(HgRepository.NO_REVISION, NULL, csetRevIndex); + * followed immediately by Inspector.end(HgRepository.NO_REVISION) + * when start and/or end point to changeset with no associated * manifest revision. However, if changeset-manifest revision pairs look like: *

 	 *   3  8
@@ -148,14 +146,14 @@
 	 * call walk(3,5, insp) would yield only (3,8) and (5,9) to the inspector, without additional empty 
 	 * Inspector.begin(); Inspector.end() call pair.   
 	 * 
+	 * @see HgRepository#NO_REVISION
 	 * @param start changelog (not manifest!) revision to begin with
 	 * @param end changelog (not manifest!) revision to stop, inclusive.
 	 * @param inspector manifest revision visitor, can't be null
-	 * @throws HgInvalidRevisionException if start or end specify non-existent revision index
-	 * @throws IllegalArgumentException if start or end is not a revision index
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
+	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception
+	 * @throws IllegalArgumentException if inspector callback is null
 	 */
-	public void walk(int start, int end, final Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException {
+	public void walk(int start, int end, final Inspector inspector) throws HgRuntimeException, IllegalArgumentException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
@@ -164,8 +162,8 @@
 		do {
 			manifestFirst = fromChangelog(csetFirst+i);
 			if (manifestFirst == BAD_REVISION) {
-				inspector.begin(BAD_REVISION, NULL, csetFirst+i);
-				inspector.end(BAD_REVISION);
+				inspector.begin(NO_REVISION, NULL, csetFirst+i);
+				inspector.end(NO_REVISION);
 			}
 			i++;
 		} while (manifestFirst == BAD_REVISION && csetFirst+i <= csetLast);
@@ -179,15 +177,15 @@
 		do {
 			manifestLast = fromChangelog(csetLast-i);
 			if (manifestLast == BAD_REVISION) {
-				inspector.begin(BAD_REVISION, NULL, csetLast-i);
-				inspector.end(BAD_REVISION);
+				inspector.begin(NO_REVISION, NULL, csetLast-i);
+				inspector.end(NO_REVISION);
 			}
 			i++;
 		} while (manifestLast == BAD_REVISION && csetLast-i >= csetFirst);
 		if (manifestLast == BAD_REVISION) {
-			// hmm, manifestFirst != -1 here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, 
-			// and thus it's impossible to run into manifestLast == -1. Nevertheless, never hurts to check.
-			throw new HgBadStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast));
+			// hmm, manifestFirst != BAD_REVISION here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, 
+			// and thus it's impossible to run into manifestLast == BAD_REVISION. Nevertheless, never hurts to check.
+			throw new HgInvalidStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast));
 		}
 		if (manifestLast < manifestFirst) {
 			// there are tool-constructed repositories that got order of changeset revisions completely different from that of manifest
@@ -207,10 +205,10 @@
 	 * 
 	 * @param inspector manifest revision visitor, can't be null
 	 * @param revisionIndexes local indexes of changesets to visit, non-null
-	 * @throws HgInvalidRevisionException if argument specifies non-existent revision index
-	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
+	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception
+	 * @throws InvalidArgumentException if supplied arguments are nulls
 	 */
-	public void walk(final Inspector inspector, int... revisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException {
+	public void walk(final Inspector inspector, int... revisionIndexes) throws HgRuntimeException, IllegalArgumentException {
 		if (inspector == null || revisionIndexes == null) {
 			throw new IllegalArgumentException();
 		}
@@ -310,8 +308,8 @@
 			final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]);
 			if (manifestRevisionIndex == BAD_REVISION) {
 				if (inspector != null) {
-					inspector.begin(BAD_REVISION, NULL, changelogRevisionIndexes[i]);
-					inspector.end(BAD_REVISION);
+					inspector.begin(NO_REVISION, NULL, changelogRevisionIndexes[i]);
+					inspector.end(NO_REVISION);
 				}
 				// othrwise, ignore changeset without manifest
 			} else {
@@ -335,6 +333,7 @@
 		}
 	}
 
+	@Callback
 	public interface Inspector {
 		boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision);
 		/**
@@ -346,6 +345,7 @@
 	}
 	
 	@Experimental(reason="Explore Path alternative for filenames and enum for flags")
+	@Callback
 	public interface Inspector2 extends Inspector {
 		/**
 		 * @param nid file revision
@@ -448,7 +448,7 @@
 			progressHelper = ProgressSupport.Factory.get(delegate);
 		}
 		
-		public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) throws HgException {
+		public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
 			try {
 				if (!inspector.begin(revisionNumber, new Nodeid(nodeid, true), linkRevision)) {
 					iterateControl.stop();
@@ -525,7 +525,7 @@
 				iterateControl.checkCancelled();
 				progressHelper.worked(1);
 			} catch (IOException ex) {
-				throw new HgException(ex);
+				throw new HgInvalidControlFileException("Failed reading manifest", ex, null).setRevisionIndex(revisionNumber);
 			}
 		}
 
@@ -613,19 +613,14 @@
 				}
 			}
 			for (int u : undefinedChangelogRevision) {
-				try {
-					Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest();
-					// TODO calculate those missing effectively (e.g. cache and sort nodeids to speed lookup
-					// right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here)
-					if (manifest.isNull()) {
-						repo.getContext().getLog().warn(getClass(), "Changeset %d has no associated manifest entry", u);
-						// keep -1 in the changelog2manifest map.
-					} else {
-						changelog2manifest[u] = repo.getManifest().getRevisionIndex(manifest);
-					}
-				} catch (HgInvalidControlFileException ex) {
-					// FIXME EXCEPTIONS need to propagate the error up to client  
-					repo.getContext().getLog().error(getClass(), ex, null);
+				Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest();
+				// TODO calculate those missing effectively (e.g. cache and sort nodeids to speed lookup
+				// right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here)
+				if (manifest.isNull()) {
+					repo.getContext().getLog().warn(getClass(), "Changeset %d has no associated manifest entry", u);
+					// keep -1 in the changelog2manifest map.
+				} else {
+					changelog2manifest[u] = repo.getManifest().getRevisionIndex(manifest);
 				}
 			}
 		}
@@ -649,7 +644,7 @@
 			filenameAsBytes = eh.toManifest(fileToLookUp.toString());
 		}
 		
-		public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException {
+		public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) {
 			ByteArrayOutputStream bos = new ByteArrayOutputStream();
 			try {
 				byte b;
@@ -689,7 +684,7 @@
 					}
 				}
 			} catch (IOException ex) {
-				throw new HgException(ex); // FIXME EXCEPTIONS
+				throw new HgInvalidControlFileException("Failed reading manifest", ex, null);
 			}
 		}
 	}
diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgMergeState.java
--- a/src/org/tmatesoft/hg/repo/HgMergeState.java	Fri Mar 23 21:26:01 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgMergeState.java	Fri Mar 23 22:51:18 2012 +0100
@@ -27,9 +27,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.tmatesoft.hg.core.HgBadStateException;
 import org.tmatesoft.hg.core.HgFileRevision;
-import org.tmatesoft.hg.core.HgInvalidControlFileException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.ManifestRevision;
 import org.tmatesoft.hg.internal.Pool;
@@ -39,7 +37,8 @@
 import org.tmatesoft.hg.util.PathRewrite;
 
 /**
- *
+ * Access to repository's merge state
+ * 
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
@@ -92,7 +91,11 @@
 		repo = hgRepo;
 	}
 
-	public void refresh() throws HgInvalidControlFileException {
+	/**
+	 * Update our knowledge about repository's merge state
+	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception
+	 */
+	public void refresh() throws HgRuntimeException {
 		entries = null;
 		// it's possible there are two parents but no merge/state, we shall report this case as 'merging', with proper
 		// first and second parent values
@@ -153,7 +156,7 @@
 				} else if ("r".equals(r[1])) {
 					k = Kind.Resolved;
 				} else {
-					throw new HgBadStateException(r[1]);
+					throw new HgInvalidStateException(String.format("Unknown merge kind %s", r[1]));
 				}
 				Entry e = new Entry(k, pathPool.path(r[0]), p1, p2, ca);
 				result.add(e);
@@ -184,7 +187,7 @@
 	 */
 	public boolean isStale() {
 		if (wcp1 == null) {
-			throw new HgBadStateException("Call #refresh() first");
+			refresh();
 		}
 		return !stateParent.isNull() /*there's merge state*/ && !wcp1.equals(stateParent) /*and it doesn't match*/; 
 	}
@@ -192,11 +195,12 @@
 	/**
 	 * It's possible for a repository to be in a 'merging' state (@see {@link #isMerging()} without any
 	 * conflict to resolve (no merge state information file).
+	 * 
 	 * @return first parent of the working copy, never null
 	 */
 	public Nodeid getFirstParent() {
 		if (wcp1 == null) {
-			throw new HgBadStateException("Call #refresh() first");
+			refresh();
 		}
 		return wcp1;
 	}
@@ -206,7 +210,7 @@
 	 */
 	public Nodeid getSecondParent() {
 		if (wcp2 == null) {
-			throw new HgBadStateException("Call #refresh() first");
+			refresh();
 		}
 		return wcp2;
 	}
@@ -216,7 +220,7 @@
 	 */
 	public Nodeid getStateParent() {
 		if (stateParent == null) {
-			throw new HgBadStateException("Call #refresh() first");
+			refresh();
 		}
 		return stateParent;
 	}
diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgRemoteRepository.java
--- a/src/org/tmatesoft/hg/repo/HgRemoteRepository.java	Fri Mar 23 21:26:01 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRemoteRepository.java	Fri Mar 23 22:51:18 2012 +0100
@@ -47,8 +47,6 @@
 import javax.net.ssl.X509TrustManager;
 
 import org.tmatesoft.hg.core.HgBadArgumentException;
-import org.tmatesoft.hg.core.HgBadStateException;
-import org.tmatesoft.hg.core.HgInvalidFileException;
 import org.tmatesoft.hg.core.HgRemoteConnectionException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.SessionContext;
@@ -234,12 +232,12 @@
 						assert currRange == null;
 						assert currRangeList == null;
 						if (!rangeItr.hasNext()) {
-							throw new HgBadStateException("Internal error");
+							throw new HgInvalidStateException("Internal error"); // TODO revisit-1.1
 						}
 						rv.put(rangeItr.next(), Collections.emptyList());
 					} else {
 						if (currRange == null || currRangeList == null) {
-							throw new HgBadStateException("Internal error");
+							throw new HgInvalidStateException("Internal error"); // TODO revisit-1.1
 						}
 						// indicate next range value is needed
 						currRange = null;
@@ -250,7 +248,7 @@
 					possiblyEmptyNextLine = false;
 					if (currRange == null) {
 						if (!rangeItr.hasNext()) {
-							throw new HgBadStateException("Internal error");
+							throw new HgInvalidStateException("Internal error"); // TODO revisit-1.1
 						}
 						currRange = rangeItr.next();
 						currRangeList = new LinkedList();
diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgRepository.java
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Fri Mar 23 21:26:01 2012 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Fri Mar 23 22:51:18 2012 +0100
@@ -26,8 +26,6 @@
 import java.util.List;
 
 import org.tmatesoft.hg.core.HgException;
-import org.tmatesoft.hg.core.HgInvalidControlFileException;
-import org.tmatesoft.hg.core.HgInvalidRevisionException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.core.SessionContext;
 import org.tmatesoft.hg.internal.ByteArrayChannel;
@@ -75,9 +73,14 @@
 	public static final int WORKING_COPY = -2; // XXX WORKING_COPY_REVISION?
 	
 	/**
-	 * Constant ({@value #NO_REVISION}) to indicate revision absence (e.g. missing parent in from {@link HgChangelog#parents(int, int[], byte[], byte[])} call) 
-	 * or a fictitious revision of an empty repository, to use as an argument (contrary to {@link #BAD_REVISION})
-	 * e.g in a status operation to visit changes from the very beginning of a repository. 
+	 * Constant ({@value #NO_REVISION}) to indicate revision absence or a fictitious revision of an empty repository.
+	 * 
+	 * 

Revision absence is vital e.g. for missing parent from {@link HgChangelog#parents(int, int[], byte[], byte[])} call and + * to report cases when changeset records no corresponding manifest + * revision {@link HgManifest#walk(int, int, org.tmatesoft.hg.repo.HgManifest.Inspector)}. + * + *

Use as imaginary revision/empty repository is handy as an argument (contrary to {@link #BAD_REVISION}) + * e.g in a status operation to visit changes from the very beginning of a repository. */ public static final int NO_REVISION = -1; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgRuntimeException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgRuntimeException.java Fri Mar 23 22:51:18 2012 +0100 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.ExceptionInfo; +import org.tmatesoft.hg.util.Path; + +/** + * Almost any method in Hg4J low-level API may throw subclass of this exception to indicate unexpected + * state/condition encountered, flawed data or IO error. Since most cases can't be handled in a reasonable + * manner (other than catch all exceptions and tell client something went wrong), and propagating all possible + * exceptions up through API is dubious task, low-level exceptions are made runtime, rooting at this single class. + * + *

Hi-level api, {@link org.tmatesoft.hg.core}, where interaction with user-supplied values is more explicit, + * may follow different exception strategy. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@SuppressWarnings("serial") +public abstract class HgRuntimeException extends RuntimeException { + + protected final ExceptionInfo details = new ExceptionInfo(this); + + protected HgRuntimeException(String reason, Throwable cause) { + super(reason, cause); + } + + /** + * @return {@link HgRepository#BAD_REVISION} unless revision index was set during exception instantiation + */ + public int getRevisionIndex() { + return details.getRevisionIndex(); + } + + public HgRuntimeException setRevisionIndex(int rev) { + return details.setRevisionIndex(rev); + } + + public boolean isRevisionIndexSet() { + return details.isRevisionIndexSet(); + } + + + /** + * @return non-null when revision was supplied at construction time + */ + public Nodeid getRevision() { + return details.getRevision(); + } + + public HgRuntimeException setRevision(Nodeid r) { + return details.setRevision(r); + } + + public boolean isRevisionSet() { + return details.isRevisionSet(); + } + + /** + * @return non-null only if file name was set at construction time + */ + public Path getFileName() { + return details.getFileName(); + } + + public HgRuntimeException setFileName(Path name) { + return details.setFileName(name); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(super.toString()); + sb.append(' '); + sb.append('('); + details.appendDetails(sb); + sb.append(')'); + return sb.toString(); + } +} diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgStatusCollector.java --- a/src/org/tmatesoft/hg/repo/HgStatusCollector.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgStatusCollector.java Fri Mar 23 22:51:18 2012 +0100 @@ -26,21 +26,20 @@ import java.util.Map; import java.util.TreeSet; -import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.IntMap; import org.tmatesoft.hg.internal.ManifestRevision; import org.tmatesoft.hg.internal.Pool; +import org.tmatesoft.hg.util.CancelSupport; +import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Path; import org.tmatesoft.hg.util.PathPool; import org.tmatesoft.hg.util.PathRewrite; /** - * RevisionWalker? + * Collect status information for changes between two repository revisions. * * @author Artem Tikhomirov * @author TMate Software Ltd. @@ -130,7 +129,7 @@ } public boolean next(Nodeid nid, String fname, String flags) { - throw new HgBadStateException(HgManifest.Inspector2.class.getName()); + throw new IllegalStateException(HgManifest.Inspector2.class.getName()); } public boolean next(Nodeid nid, Path fname, HgManifest.Flags flags) { @@ -193,10 +192,10 @@ /** * 'hg status --change REV' command counterpart. * - * @throws HgInvalidRevisionException if argument specifies non-existent revision index - * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws CancelledException if operation execution was cancelled + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ - public void change(int revisionIndex, HgStatusInspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { + public void change(int revisionIndex, HgStatusInspector inspector) throws CancelledException, HgRuntimeException { int p; if (revisionIndex == 0) { p = NO_REVISION; @@ -213,16 +212,14 @@ * Parameters rev1 and rev2 are changelog revision indexes, shall not be the same. Argument order matters. * Either rev1 or rev2 may be {@link HgRepository#NO_REVISION} to indicate comparison to empty repository * - * FIXME cancellation (at least exception)? - * * @param rev1 from changeset index, non-negative or {@link HgRepository#TIP} * @param rev2 to changeset index, non-negative or {@link HgRepository#TIP} * @param inspector callback for status information - * @throws HgInvalidRevisionException if any argument specifies non-existent revision index + * @throws CancelledException if operation execution was cancelled + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception * @throws IllegalArgumentException inspector other incorrect argument values - * @throws HgInvalidControlFileException if access to revlog index/data entry failed */ - public void walk(int rev1, int rev2, HgStatusInspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { + public void walk(int rev1, int rev2, HgStatusInspector inspector) throws CancelledException, HgRuntimeException, IllegalArgumentException { if (rev1 == rev2) { throw new IllegalArgumentException(); } @@ -278,6 +275,8 @@ r1 = get(rev1); r2 = get(rev2); + final CancelSupport cs = CancelSupport.Factory.get(inspector); + TreeSet r1Files = new TreeSet(r1.files()); for (Path r2fname : r2.files()) { if (!scope.accept(r2fname)) { @@ -293,6 +292,7 @@ } else { inspector.modified(r2fname); } + cs.checkCancelled(); } else { try { Path copyTarget = r2fname; @@ -307,11 +307,13 @@ // for a single file not to be irresolvable obstacle for a status operation inspector.invalid(r2fname, ex); } + cs.checkCancelled(); } } for (Path r1fname : r1Files) { if (scope.accept(r1fname)) { inspector.removed(r1fname); + cs.checkCancelled(); } } } @@ -322,12 +324,18 @@ * @param rev1 from changeset index * @param rev2 to changeset index * @return information object that describes change between the revisions - * @throws HgInvalidRevisionException if any argument specifies non-existent revision index - * @throws HgInvalidControlFileException if access to revlog index/data entry failed + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception */ public Record status(int rev1, int rev2) throws HgInvalidRevisionException, HgInvalidControlFileException { Record rv = new Record(); - walk(rev1, rev2, rv); + try { + walk(rev1, rev2, rv); + } catch (CancelledException ex) { + // can't happen as long our Record class doesn't implement CancelSupport + HgInvalidStateException t = new HgInvalidStateException("Internal error"); + t.initCause(ex); + throw t; + } return rv; } @@ -368,6 +376,7 @@ * from {@link #getAdded()}. */ public static class Record implements HgStatusInspector { + // NOTE, shall not implement CancelSupport, or methods that use it and don't expect this exception shall be changed private List modified, added, removed, clean, missing, unknown, ignored; private Map copied; private Map failures; diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgSubrepoLocation.java --- a/src/org/tmatesoft/hg/repo/HgSubrepoLocation.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgSubrepoLocation.java Fri Mar 23 22:51:18 2012 +0100 @@ -18,8 +18,7 @@ import java.io.File; -import org.tmatesoft.hg.core.HgBadStateException; -import org.tmatesoft.hg.core.HgInvalidFileException; +import org.tmatesoft.hg.core.HgRepositoryNotFoundException; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.util.Path; @@ -86,9 +85,15 @@ return owner; } - public HgRepository getRepo() throws HgInvalidFileException { + /** + * + * @return object to access sub-repository + * @throws HgRepositoryNotFoundException if failed to find repository + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + */ + public HgRepository getRepo() throws HgRepositoryNotFoundException { if (kind != Kind.Hg) { - throw new HgBadStateException(String.format("Unsupported subrepository %s", kind)); + throw new HgInvalidStateException(String.format("Unsupported subrepository %s", kind)); } return new HgLookup().detect(new File(owner.getWorkingDir(), source)); } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgTags.java --- a/src/org/tmatesoft/hg/repo/HgTags.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgTags.java Fri Mar 23 22:51:18 2012 +0100 @@ -29,7 +29,6 @@ import java.util.Map; import java.util.TreeMap; -import org.tmatesoft.hg.core.HgInvalidControlFileException; import org.tmatesoft.hg.core.Nodeid; /** diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java --- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java Fri Mar 23 22:51:18 2012 +0100 @@ -31,8 +31,6 @@ import java.util.TreeSet; import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidFileException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ByteArrayChannel; import org.tmatesoft.hg.internal.Experimental; @@ -43,6 +41,7 @@ import org.tmatesoft.hg.internal.Preview; import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.ByteChannel; +import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.FileInfo; import org.tmatesoft.hg.util.FileIterator; @@ -151,9 +150,17 @@ return dirstateParentManifest; } - // may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent - // NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent. - public void walk(int baseRevision, HgStatusInspector inspector) throws HgInvalidControlFileException, IOException { + /** + * may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent + * XXX NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent. + * + * @param baseRevision + * @param inspector + * @throws IOException to propagate IO errors from {@link FileIterator} + * @throws CancelledException if operation execution was cancelled + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + */ + public void walk(int baseRevision, HgStatusInspector inspector) throws IOException, CancelledException, HgRuntimeException { if (HgInternals.wrongRevisionIndex(baseRevision) || baseRevision == BAD_REVISION) { throw new IllegalArgumentException(String.valueOf(baseRevision)); } @@ -184,12 +191,14 @@ } ((HgStatusCollector.Record) inspector).init(rev1, rev2, sc); } + final CancelSupport cs = CancelSupport.Factory.get(inspector); final HgIgnore hgIgnore = repo.getIgnore(); repoWalker.reset(); TreeSet processed = new TreeSet(); // names of files we handled as they known to Dirstate (not FileIterator) final HgDirstate ds = getDirstateImpl(); TreeSet knownEntries = ds.all(); // here just to get dirstate initialized while (repoWalker.hasNext()) { + cs.checkCancelled(); repoWalker.next(); final Path fname = getPathPool().path(repoWalker.name()); FileInfo f = repoWalker.file(); @@ -250,6 +259,7 @@ for (Path fromBase : baseRevFiles) { if (repoWalker.inScope(fromBase)) { inspector.removed(fromBase); + cs.checkCancelled(); } } } @@ -259,6 +269,7 @@ // do not report as missing/removed those FileIterator doesn't care about. continue; } + cs.checkCancelled(); // missing known file from a working dir if (ds.checkRemoved(m) == null) { // not removed from the repository = 'deleted' @@ -273,9 +284,24 @@ } } - public HgStatusCollector.Record status(int baseRevision) throws HgInvalidControlFileException, IOException { + /** + * + * @param baseRevision + * @return information object that describes change between the revisions + * @throws IOException to propagate IO errors from {@link FileIterator} + * @throws CancelledException if operation execution was cancelled + * @throws HgRuntimeException subclass thereof to indicate issues with the library. Runtime exception + */ + public HgStatusCollector.Record status(int baseRevision) throws IOException, HgRuntimeException { HgStatusCollector.Record rv = new HgStatusCollector.Record(); - walk(baseRevision, rv); + try { + walk(baseRevision, rv); + } catch (CancelledException ex) { + // can't happen as long our Record class doesn't implement CancelSupport + HgInvalidStateException t = new HgInvalidStateException("Internal error"); + t.initCause(ex); + throw t; + } return rv; } @@ -430,7 +456,7 @@ // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean' } - private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) throws HgException { + private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) throws HgException, HgInvalidFileException { // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison ByteArrayChannel bac = new ByteArrayChannel(); try { @@ -444,7 +470,7 @@ return areTheSame(f, bac.toArray(), dataFile.getPath()); } - private boolean areTheSame(FileInfo f, final byte[] data, Path p) throws HgException { + private boolean areTheSame(FileInfo f, final byte[] data, Path p) throws HgInvalidFileException { ReadableByteChannel is = null; class Check implements ByteChannel { final boolean debug = repo.getContext().getLog().isDebug(); @@ -666,13 +692,11 @@ } public boolean supportsExecFlag() { - // TODO Auto-generated method stub - return false; + return execCap; } public boolean supportsLinkFlag() { - // TODO Auto-generated method stub - return false; + return linkCap; } } diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/repo/Revlog.java --- a/src/org/tmatesoft/hg/repo/Revlog.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Fri Mar 23 22:51:18 2012 +0100 @@ -29,10 +29,7 @@ import java.util.LinkedList; import java.util.List; -import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.HgException; -import org.tmatesoft.hg.core.HgInvalidControlFileException; -import org.tmatesoft.hg.core.HgInvalidRevisionException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; import org.tmatesoft.hg.internal.DataAccess; @@ -172,7 +169,7 @@ } if (rn < 0 || rn >= content.revisionCount()) { // Sanity check - throw new HgBadStateException(String.format("Revision index %d found for nodeid %s is not from the range [0..%d]", rn, nodeid.shortNotation(), content.revisionCount()-1)); + throw new HgInvalidStateException(String.format("Revision index %d found for nodeid %s is not from the range [0..%d]", rn, nodeid.shortNotation(), content.revisionCount()-1)); } return true; } @@ -399,7 +396,7 @@ private void assertSortedIndex(int x) { if (x < 0) { - throw new HgBadStateException(String.format("Bad index", x)); + throw new HgInvalidStateException(String.format("Bad index", x)); } } @@ -617,6 +614,7 @@ failure = ex; } + // FIXME is HgException of any use here now? // TODO consider if IOException in addition to HgException is of any real utility public void checkFailed() throws HgException, IOException, CancelledException { if (failure == null) { @@ -631,7 +629,7 @@ if (failure instanceof HgException) { throw (HgException) failure; } - throw new HgBadStateException(failure); + throw new HgInvalidStateException(failure.toString()); } public void checkCancelled() throws CancelledException { @@ -697,7 +695,7 @@ logFacility.warn(getClass(), "Bad data sink when reading revision %d. Reported %d bytes consumed, byt actually read %d", revisionNumber, consumed, buf.position()); } if (buf.position() == 0) { - throw new HgBadStateException("Bad sink implementation (consumes no bytes) results in endless loop"); + throw new HgInvalidStateException("Bad sink implementation (consumes no bytes) results in endless loop"); } buf.compact(); // ensure (a) there's space for new (b) data starts at 0 progressSupport.worked(consumed); diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/util/CancelSupport.java --- a/src/org/tmatesoft/hg/util/CancelSupport.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/util/CancelSupport.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,10 +47,11 @@ if (cs != null) { return cs; } - return new CancelSupport() { + class NoCancel implements CancelSupport { public void checkCancelled() { } }; + return new NoCancel(); } public static CancelSupport get(Object target, CancelSupport defaultValue) { diff -r 5d1cc7366d04 -r 9c9c442b5f2e src/org/tmatesoft/hg/util/RegularFileStats.java --- a/src/org/tmatesoft/hg/util/RegularFileStats.java Fri Mar 23 21:26:01 2012 +0100 +++ b/src/org/tmatesoft/hg/util/RegularFileStats.java Fri Mar 23 22:51:18 2012 +0100 @@ -46,7 +46,7 @@ * TODO post-1.0 Add extraction of link modification time, see RegularFileInfo#lastModified() * * @author Artem Tikhomirov - * @author Tmate Software Ltd. + * @author TMate Software Ltd. */ /*package-local*/ class RegularFileStats { private boolean isExec, isSymlink; diff -r 5d1cc7366d04 -r 9c9c442b5f2e test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java --- a/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java Fri Mar 23 21:26:01 2012 +0100 +++ b/test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java Fri Mar 23 22:51:18 2012 +0100 @@ -11,7 +11,7 @@ import java.util.Map; import org.junit.Assert; -import org.tmatesoft.hg.core.HgBadStateException; +import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgChangeset; import org.tmatesoft.hg.core.HgChangesetHandler; import org.tmatesoft.hg.core.HgException; @@ -122,11 +122,7 @@ fileNode.walk(0, TIP, new HgDataFile.RevisionInspector() { public void next(int fileRevisionIndex, Nodeid revision, int linkedRevisionIndex) { - try { - changesetToNodeid_3.put(clog.getRevision(linkedRevisionIndex), revision); - } catch (HgException ex) { - ex.printStackTrace(); - } + changesetToNodeid_3.put(clog.getRevision(linkedRevisionIndex), revision); } }); final long end_3 = System.nanoTime(); @@ -242,8 +238,8 @@ // // build cache // - final TagInfo[] allTags = new TagInfo[tags.getTags().size()]; - tags.getTags().values().toArray(allTags); + final TagInfo[] allTags = new TagInfo[tags.getAllTags().size()]; + tags.getAllTags().values().toArray(allTags); // effective translation of changeset revisions to their local indexes final HgChangelog.RevisionMap clogrmap = repository.getChangelog().new RevisionMap().init(); // map to look up tag by changeset local number @@ -295,7 +291,7 @@ } public boolean next(Nodeid nid, String fname, String flags) { - throw new HgBadStateException(HgManifest.Inspector2.class.getName()); + throw new IllegalStateException(HgManifest.Inspector2.class.getName()); } public boolean next(Nodeid nid, Path fname, HgManifest.Flags flags) { @@ -379,7 +375,7 @@ return true; } public boolean next(Nodeid nid, String fname, String flags) { - throw new HgBadStateException(HgManifest.Inspector2.class.getName()); + throw new IllegalStateException(HgManifest.Inspector2.class.getName()); } public boolean next(Nodeid nid, Path fname, Flags flags) { return true; @@ -389,11 +385,11 @@ } } - public static void main2(String[] args) throws HgException, CancelledException { + public static void main2(String[] args) throws HgCallbackTargetException, HgException, CancelledException { final HgRepository repository = new HgLookup().detect(new File("/temp/hg/cpython")); final Path targetPath = Path.create("README"); final HgTags tags = repository.getTags(); - final Map tagToInfo = tags.getTags(); + final Map tagToInfo = tags.getAllTags(); final HgManifest manifest = repository.getManifest(); final Map> changeSetRevisionToTags = new HashMap>(); final HgDataFile fileNode = repository.getFileNode(targetPath); @@ -420,7 +416,7 @@ logCommand.execute(new HgChangesetHandler() { public void next(HgChangeset changeset) { if (changeset.getAffectedFiles().contains(targetPath)) { - System.out.println(changeset.getRevision() + " " + changeSetRevisionToTags.get(changeset.getNodeid())); + System.out.println(changeset.getRevisionIndex() + " " + changeSetRevisionToTags.get(changeset.getNodeid())); } } }); diff -r 5d1cc7366d04 -r 9c9c442b5f2e test/org/tmatesoft/hg/test/TestAuxUtilities.java --- a/test/org/tmatesoft/hg/test/TestAuxUtilities.java Fri Mar 23 21:26:01 2012 +0100 +++ b/test/org/tmatesoft/hg/test/TestAuxUtilities.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ import org.junit.Ignore; import org.junit.Test; import org.tmatesoft.hg.core.HgCatCommand; -import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; import org.tmatesoft.hg.repo.HgChangelog; @@ -235,13 +234,9 @@ int i = 0; public void next(int localRevision, Nodeid revision, int linkedRevision) { - try { - Assert.assertEquals(i++, localRevision); - Assert.assertEquals(fileNode.getChangesetRevisionIndex(localRevision), linkedRevision); - Assert.assertEquals(fileNode.getRevision(localRevision), revision); - } catch (HgException ex) { - Assert.fail(ex.toString()); - } + Assert.assertEquals(i++, localRevision); + Assert.assertEquals(fileNode.getChangesetRevisionIndex(localRevision), linkedRevision); + Assert.assertEquals(fileNode.getRevision(localRevision), revision); } }); fileNode.walk(0, TIP, new HgDataFile.ParentInspector() { diff -r 5d1cc7366d04 -r 9c9c442b5f2e test/org/tmatesoft/hg/test/TestHistory.java --- a/test/org/tmatesoft/hg/test/TestHistory.java Fri Mar 23 21:26:01 2012 +0100 +++ b/test/org/tmatesoft/hg/test/TestHistory.java Fri Mar 23 22:51:18 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -113,7 +113,7 @@ final LinkedList sorted = new LinkedList(h.getChanges()); Collections.sort(sorted, new Comparator() { public int compare(HgChangeset cs1, HgChangeset cs2) { - return cs1.getRevision() < cs2.getRevision() ? 1 : -1; + return cs1.getRevisionIndex() < cs2.getRevisionIndex() ? 1 : -1; } }); report(what, sorted, false); @@ -137,14 +137,14 @@ break; } Record cr = consoleResultItr.next(); - int x = cs.getRevision() == cr.changesetIndex ? 0x1 : 0; + int x = cs.getRevisionIndex() == cr.changesetIndex ? 0x1 : 0; x |= cs.getDate().toString().equals(cr.date) ? 0x2 : 0; x |= cs.getNodeid().toString().equals(cr.changesetNodeid) ? 0x4 : 0; x |= cs.getUser().equals(cr.user) ? 0x8 : 0; // need to do trim() on comment because command-line template does, and there are // repositories that have couple of newlines in the end of the comment (e.g. hello sample repo from the book) x |= cs.getComment().trim().equals(cr.description) ? 0x10 : 0; - errorCollector.checkThat(String.format(what + ". Mismatch (0x%x) in %d hg4j rev comparing to %d cmdline's.", x, cs.getRevision(), cr.changesetIndex), x, equalTo(0x1f)); + errorCollector.checkThat(String.format(what + ". Mismatch (0x%x) in %d hg4j rev comparing to %d cmdline's.", x, cs.getRevisionIndex(), cr.changesetIndex), x, equalTo(0x1f)); consoleResultItr.remove(); } errorCollector.checkThat(what + ". Unprocessed results in console left (insufficient from hg4j)", consoleResultItr.hasNext(), equalTo(false));