Mercurial > hg4j
changeset 312:f9f3e9b67ccc
Facilitate cancellation and progress reporting in changelog and manifest iterations
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 27 Sep 2011 05:29:12 +0200 |
parents | b9592e21176a |
children | c1e3c18fd2f2 |
files | src/org/tmatesoft/hg/core/HgChangesetHandler.java src/org/tmatesoft/hg/internal/IterateControlMediator.java src/org/tmatesoft/hg/repo/HgChangelog.java src/org/tmatesoft/hg/repo/HgManifest.java src/org/tmatesoft/hg/util/CancelSupport.java test/org/tmatesoft/hg/test/TestAuxUtilities.java |
diffstat | 6 files changed, 206 insertions(+), 16 deletions(-) [+] |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/core/HgChangesetHandler.java Mon Sep 26 04:06:04 2011 +0200 +++ b/src/org/tmatesoft/hg/core/HgChangesetHandler.java Tue Sep 27 05:29:12 2011 +0200 @@ -16,6 +16,8 @@ */ package org.tmatesoft.hg.core; +import org.tmatesoft.hg.util.CancelledException; + /** * Callback to process {@link HgChangeset changesets}. * @@ -25,8 +27,9 @@ public interface HgChangesetHandler/*XXX perhaps, shall parameterize with exception clients can throw, like: <E extends Exception>*/ { /** * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy. + * @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 E*/; + void next(HgChangeset changeset) throws CancelledException; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/IterateControlMediator.java Tue Sep 27 05:29:12 2011 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 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 org.tmatesoft.hg.internal.Lifecycle.Callback; +import org.tmatesoft.hg.util.CancelSupport; +import org.tmatesoft.hg.util.CancelledException; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class IterateControlMediator { + + private final CancelSupport src; + private Callback receiver; + + public IterateControlMediator(CancelSupport source, Lifecycle.Callback target) { + assert target != null; + src = source; + receiver = target; + } + + public boolean checkCancelled() { + if (src == null) { + return false; + } + try { + src.checkCancelled(); + return false; + } catch (CancelledException ex) { + receiver.stop(); + return true; + } + } + + public void stop() { + receiver.stop(); + } +}
--- a/src/org/tmatesoft/hg/repo/HgChangelog.java Mon Sep 26 04:06:04 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgChangelog.java Tue Sep 27 05:29:12 2011 +0200 @@ -33,8 +33,12 @@ import org.tmatesoft.hg.core.HgBadStateException; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.DataAccess; +import org.tmatesoft.hg.internal.IterateControlMediator; +import org.tmatesoft.hg.internal.Lifecycle; import org.tmatesoft.hg.internal.Pool; import org.tmatesoft.hg.internal.RevlogStream; +import org.tmatesoft.hg.util.CancelSupport; +import org.tmatesoft.hg.util.ProgressSupport; /** * Representation of the Mercurial changelog file (list of ChangeSets) @@ -338,16 +342,19 @@ } } - private static class RawCsetParser implements RevlogStream.Inspector { + private static class RawCsetParser implements RevlogStream.Inspector, Lifecycle { private final Inspector inspector; private final Pool<String> usersPool; private final RawChangeset cset = new RawChangeset(); + private final ProgressSupport progressHelper; + private IterateControlMediator iterateControl; public RawCsetParser(HgChangelog.Inspector delegate) { assert delegate != null; inspector = delegate; usersPool = new Pool<String>(); + progressHelper = ProgressSupport.Factory.get(delegate); } public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { @@ -356,9 +363,23 @@ 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 (Exception ex) { throw new HgBadStateException(ex); // FIXME exception handling } + if (iterateControl != null) { + iterateControl.checkCancelled(); + } + } + + public void start(int count, Callback callback, Object token) { + CancelSupport cs = CancelSupport.Factory.get(inspector, null); + iterateControl = cs == null ? null : new IterateControlMediator(cs, callback); + progressHelper.start(count); + } + + public void finish(Object token) { + progressHelper.done(); } } }
--- a/src/org/tmatesoft/hg/repo/HgManifest.java Mon Sep 26 04:06:04 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgManifest.java Tue Sep 27 05:29:12 2011 +0200 @@ -28,10 +28,13 @@ import org.tmatesoft.hg.internal.DataAccess; import org.tmatesoft.hg.internal.DigestHelper; import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.internal.IterateControlMediator; import org.tmatesoft.hg.internal.Lifecycle; import org.tmatesoft.hg.internal.Pool2; import org.tmatesoft.hg.internal.RevlogStream; +import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.Path; +import org.tmatesoft.hg.util.ProgressSupport; /** @@ -277,13 +280,14 @@ } } - private static class ManifestParser implements RevlogStream.Inspector { - private boolean gtg = true; // good to go + private static class ManifestParser implements RevlogStream.Inspector, Lifecycle { private final Inspector inspector; private final Inspector2 inspector2; private Pool2<Nodeid> nodeidPool, thisRevPool; private final Pool2<PathProxy> fnamePool; private byte[] nodeidLookupBuffer = new byte[20]; // get reassigned each time new Nodeid is added to pool + private final ProgressSupport progressHelper; + private IterateControlMediator iterateControl; public ManifestParser(Inspector delegate) { assert delegate != null; @@ -292,20 +296,21 @@ nodeidPool = new Pool2<Nodeid>(); fnamePool = new Pool2<PathProxy>(); thisRevPool = new Pool2<Nodeid>(); + progressHelper = ProgressSupport.Factory.get(delegate); } public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { - if (!gtg) { - return; - } try { - gtg = gtg && inspector.begin(revisionNumber, new Nodeid(nodeid, true), linkRevision); + if (!inspector.begin(revisionNumber, new Nodeid(nodeid, true), linkRevision)) { + iterateControl.stop(); + return; + } Path fname = null; Flags flags = null; Nodeid nid = null; int i; byte[] data = da.byteArray(); - for (i = 0; gtg && i < actualLen; i++) { + for (i = 0; i < actualLen; i++) { int x = i; for( ; data[i] != '\n' && i < actualLen; i++) { if (fname == null && data[i] == 0) { @@ -337,18 +342,26 @@ } else { flags = null; } + boolean good2go; if (inspector2 == null) { String flagString = flags == null ? null : flags.nativeString(); - gtg = inspector.next(nid, fname.toString(), flagString); + good2go = inspector.next(nid, fname.toString(), flagString); } else { - gtg = inspector2.next(nid, fname, flags); + good2go = inspector2.next(nid, fname, flags); + } + if (!good2go) { + iterateControl.stop(); + return; } } nid = null; fname = null; flags = null; } - gtg = gtg && inspector.end(revisionNumber); + if (!inspector.end(revisionNumber)) { + iterateControl.stop(); + return; + } // // keep only actual file revisions, found at this version // (next manifest is likely to refer to most of them, although in specific cases @@ -357,10 +370,21 @@ Pool2<Nodeid> t = nodeidPool; nodeidPool = thisRevPool; thisRevPool = t; + progressHelper.worked(1); } catch (IOException ex) { throw new HgBadStateException(ex); } } + + public void start(int count, Callback callback, Object token) { + CancelSupport cs = CancelSupport.Factory.get(inspector, null); + iterateControl = new IterateControlMediator(cs, callback); + progressHelper.start(count); + } + + public void finish(Object token) { + progressHelper.done(); + } } private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle {
--- a/src/org/tmatesoft/hg/util/CancelSupport.java Mon Sep 26 04:06:04 2011 +0200 +++ b/src/org/tmatesoft/hg/util/CancelSupport.java Tue Sep 27 05:29:12 2011 +0200 @@ -43,6 +43,17 @@ * @return target if it's capable checking cancellation status or no-op implementation that never cancels. */ public static CancelSupport get(Object target) { + CancelSupport cs = get(target, null); + if (cs != null) { + return cs; + } + return new CancelSupport() { + public void checkCancelled() { + } + }; + } + + public static CancelSupport get(Object target, CancelSupport defaultValue) { if (target instanceof CancelSupport) { return (CancelSupport) target; } @@ -52,10 +63,7 @@ return cs; } } - return new CancelSupport() { - public void checkCancelled() { - } - }; + return defaultValue; } }
--- a/test/org/tmatesoft/hg/test/TestAuxUtilities.java Mon Sep 26 04:06:04 2011 +0200 +++ b/test/org/tmatesoft/hg/test/TestAuxUtilities.java Tue Sep 27 05:29:12 2011 +0200 @@ -18,7 +18,14 @@ import org.junit.Assert; import org.junit.Test; +import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; +import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.Adaptable; +import org.tmatesoft.hg.util.CancelSupport; +import org.tmatesoft.hg.util.CancelledException; /** * @@ -52,4 +59,76 @@ } return rebuilt; } + + @Test + public void testCancelSupport() throws Exception { + HgRepository repository = Configuration.get().find("branches-1"); // any repo with more revisions + class CancelImpl implements CancelSupport { + private boolean shallStop = false; + public void stop() { + shallStop = true; + } + public void checkCancelled() throws CancelledException { + if (shallStop) { + throw new CancelledException(); + } + } + } + class InspectorImplementsCancel implements HgChangelog.Inspector, CancelSupport { + public final int when2stop; + public int lastVisitet = 0; + private final CancelImpl cancelImpl = new CancelImpl(); + + public InspectorImplementsCancel(int limit) { + when2stop = limit; + } + + public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { + lastVisitet = revisionNumber; + if (revisionNumber == when2stop) { + cancelImpl.stop(); + } + } + + public void checkCancelled() throws CancelledException { + cancelImpl.checkCancelled(); + } + }; + class InspectorImplementsAdaptable implements HgChangelog.Inspector, Adaptable { + public final int when2stop; + public int lastVisitet = 0; + private final CancelImpl cancelImpl = new CancelImpl(); + + public InspectorImplementsAdaptable(int limit) { + when2stop = limit; + } + + public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { + lastVisitet = revisionNumber; + if (revisionNumber == when2stop) { + cancelImpl.stop(); + } + } + @SuppressWarnings("unchecked") + public <T> T getAdapter(Class<T> adapterClass) { + if (CancelSupport.class == adapterClass) { + return (T) cancelImpl; + } + return null; + } + + } + // + InspectorImplementsCancel insp1; + repository.getChangelog().all(insp1= new InspectorImplementsCancel(2)); + Assert.assertEquals(insp1.when2stop, insp1.lastVisitet); + repository.getChangelog().all(insp1 = new InspectorImplementsCancel(12)); + Assert.assertEquals(insp1.when2stop, insp1.lastVisitet); + // + InspectorImplementsAdaptable insp2; + repository.getChangelog().all(insp2= new InspectorImplementsAdaptable(3)); + Assert.assertEquals(insp2.when2stop, insp2.lastVisitet); + repository.getChangelog().all(insp2 = new InspectorImplementsAdaptable(10)); + Assert.assertEquals(insp2.when2stop, insp2.lastVisitet); + } }