comparison src/org/tmatesoft/hg/core/HgManifestCommand.java @ 427:31a89587eb04

FIXMEs: consistent names, throws for commands and their handlers. Use of checked exceptions in hi-level api
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 29 Mar 2012 17:14:35 +0200
parents 6437d261048a
children 12f668401613
comparison
equal deleted inserted replaced
426:063b0663495a 427:31a89587eb04
23 import java.util.ConcurrentModificationException; 23 import java.util.ConcurrentModificationException;
24 import java.util.LinkedHashMap; 24 import java.util.LinkedHashMap;
25 import java.util.LinkedList; 25 import java.util.LinkedList;
26 import java.util.List; 26 import java.util.List;
27 27
28 import org.tmatesoft.hg.internal.Callback;
29 import org.tmatesoft.hg.repo.HgManifest; 28 import org.tmatesoft.hg.repo.HgManifest;
30 import org.tmatesoft.hg.repo.HgRepository; 29 import org.tmatesoft.hg.repo.HgRepository;
31 import org.tmatesoft.hg.repo.HgManifest.Flags; 30 import org.tmatesoft.hg.repo.HgManifest.Flags;
31 import org.tmatesoft.hg.repo.HgRuntimeException;
32 import org.tmatesoft.hg.util.CancelSupport;
33 import org.tmatesoft.hg.util.CancelledException;
32 import org.tmatesoft.hg.util.Path; 34 import org.tmatesoft.hg.util.Path;
33 import org.tmatesoft.hg.util.PathPool; 35 import org.tmatesoft.hg.util.PathPool;
34 import org.tmatesoft.hg.util.PathRewrite; 36 import org.tmatesoft.hg.util.PathRewrite;
35 37
36 38
43 public class HgManifestCommand extends HgAbstractCommand<HgManifestCommand> { 45 public class HgManifestCommand extends HgAbstractCommand<HgManifestCommand> {
44 46
45 private final HgRepository repo; 47 private final HgRepository repo;
46 private Path.Matcher matcher; 48 private Path.Matcher matcher;
47 private int startRev = 0, endRev = TIP; 49 private int startRev = 0, endRev = TIP;
48 private Handler visitor; 50 private HgManifestHandler visitor;
49 private boolean needDirs = false; 51 private boolean needDirs = false;
50 52
51 private final Mediator mediator = new Mediator(); 53 private final Mediator mediator = new Mediator();
52 54
53 public HgManifestCommand(HgRepository hgRepo) { 55 public HgManifestCommand(HgRepository hgRepo) {
95 matcher = pathMatcher; 97 matcher = pathMatcher;
96 return this; 98 return this;
97 } 99 }
98 100
99 /** 101 /**
100 * Runs the command. 102 * With all parameters set, execute the command.
103 *
101 * @param handler - callback to get the outcome 104 * @param handler - callback to get the outcome
105 * @throws HgCallbackTargetException propagated exception from the handler
106 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
107 * @throws CancelledException if execution of the command was cancelled
102 * @throws IllegalArgumentException if handler is <code>null</code> 108 * @throws IllegalArgumentException if handler is <code>null</code>
103 * @throws ConcurrentModificationException if this command is already in use (running) 109 * @throws ConcurrentModificationException if this command is already in use (running)
104 */ 110 */
105 public void execute(Handler handler) throws HgException { 111 public void execute(HgManifestHandler handler) throws HgCallbackTargetException, HgException, CancelledException {
106 if (handler == null) { 112 if (handler == null) {
107 throw new IllegalArgumentException(); 113 throw new IllegalArgumentException();
108 } 114 }
109 if (visitor != null) { 115 if (visitor != null) {
110 throw new ConcurrentModificationException(); 116 throw new ConcurrentModificationException();
111 } 117 }
112 try { 118 try {
113 visitor = handler; 119 visitor = handler;
114 mediator.start(); 120 mediator.start(getCancelSupport(handler, true));
115 repo.getManifest().walk(startRev, endRev, mediator); 121 repo.getManifest().walk(startRev, endRev, mediator);
122 mediator.checkFailure();
123 } catch (HgRuntimeException ex) {
124 throw new HgLibraryFailureException(ex);
116 } finally { 125 } finally {
117 mediator.done(); 126 mediator.done();
118 visitor = null; 127 visitor = null;
119 } 128 }
120 }
121
122 /**
123 * Callback to walk file/directory tree of a revision
124 */
125 @Callback
126 public interface Handler { // FIXME TLC
127 void begin(Nodeid manifestRevision);
128 void dir(Path p); // optionally invoked (if walker was configured to spit out directories) prior to any files from this dir and subdirs
129 void file(HgFileRevision fileRevision); // XXX allow to check p is invalid (df.exists())
130 void end(Nodeid manifestRevision);
131 } 129 }
132 130
133 // I'd rather let HgManifestCommand implement HgManifest.Inspector directly, but this pollutes API alot 131 // I'd rather let HgManifestCommand implement HgManifest.Inspector directly, but this pollutes API alot
134 private class Mediator implements HgManifest.Inspector { 132 private class Mediator implements HgManifest.Inspector {
135 // file names are likely to repeat in each revision, hence caching of Paths. 133 // file names are likely to repeat in each revision, hence caching of Paths.
136 // However, once HgManifest.Inspector switches to Path objects, perhaps global Path pool 134 // However, once HgManifest.Inspector switches to Path objects, perhaps global Path pool
137 // might be more effective? 135 // might be more effective?
138 private PathPool pathPool; 136 private PathPool pathPool;
139 private List<HgFileRevision> manifestContent; 137 private List<HgFileRevision> manifestContent;
140 private Nodeid manifestNodeid; 138 private Nodeid manifestNodeid;
141 139 private Exception failure;
142 public void start() { 140 private CancelSupport cancelHelper;
141
142 public void start(CancelSupport cs) {
143 assert cs != null;
143 // Manifest keeps normalized paths 144 // Manifest keeps normalized paths
144 pathPool = new PathPool(new PathRewrite.Empty()); 145 pathPool = new PathPool(new PathRewrite.Empty());
146 cancelHelper = cs;
145 } 147 }
146 148
147 public void done() { 149 public void done() {
148 manifestContent = null; 150 manifestContent = null;
149 pathPool = null; 151 pathPool = null;
150 } 152 }
153
154 private void recordFailure(HgCallbackTargetException ex) {
155 failure = ex;
156 }
157 private void recordCancel(CancelledException ex) {
158 failure = ex;
159 }
160
161 public void checkFailure() throws HgCallbackTargetException, CancelledException {
162 // TODO post-1.0 perhaps, can combine this code (record/checkFailure) for reuse in more classes (e.g. in Revlog)
163 if (failure instanceof HgCallbackTargetException) {
164 HgCallbackTargetException ex = (HgCallbackTargetException) failure;
165 failure = null;
166 throw ex;
167 }
168 if (failure instanceof CancelledException) {
169 CancelledException ex = (CancelledException) failure;
170 failure = null;
171 throw ex;
172 }
173 }
151 174
152 public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) { 175 public boolean begin(int manifestRevision, Nodeid nid, int changelogRevision) {
153 if (needDirs && manifestContent == null) { 176 if (needDirs && manifestContent == null) {
154 manifestContent = new LinkedList<HgFileRevision>(); 177 manifestContent = new LinkedList<HgFileRevision>();
155 } 178 }
156 visitor.begin(manifestNodeid = nid); 179 try {
157 return true; 180 visitor.begin(manifestNodeid = nid);
181 cancelHelper.checkCancelled();
182 return true;
183 } catch (HgCallbackTargetException ex) {
184 recordFailure(ex);
185 return false;
186 } catch (CancelledException ex) {
187 recordCancel(ex);
188 return false;
189 }
158 } 190 }
159 public boolean end(int revision) { 191 public boolean end(int revision) {
160 if (needDirs) { 192 try {
161 LinkedHashMap<Path, LinkedList<HgFileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<HgFileRevision>>(); 193 if (needDirs) {
162 for (HgFileRevision fr : manifestContent) { 194 LinkedHashMap<Path, LinkedList<HgFileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<HgFileRevision>>();
163 Path filePath = fr.getPath(); 195 for (HgFileRevision fr : manifestContent) {
164 Path dirPath = pathPool.parent(filePath); 196 Path filePath = fr.getPath();
165 LinkedList<HgFileRevision> revs = breakDown.get(dirPath); 197 Path dirPath = pathPool.parent(filePath);
166 if (revs == null) { 198 LinkedList<HgFileRevision> revs = breakDown.get(dirPath);
167 revs = new LinkedList<HgFileRevision>(); 199 if (revs == null) {
168 breakDown.put(dirPath, revs); 200 revs = new LinkedList<HgFileRevision>();
201 breakDown.put(dirPath, revs);
202 }
203 revs.addLast(fr);
169 } 204 }
170 revs.addLast(fr); 205 for (Path dir : breakDown.keySet()) {
206 visitor.dir(dir);
207 cancelHelper.checkCancelled();
208 for (HgFileRevision fr : breakDown.get(dir)) {
209 visitor.file(fr);
210 }
211 }
212 manifestContent.clear();
171 } 213 }
172 for (Path dir : breakDown.keySet()) { 214 visitor.end(manifestNodeid);
173 visitor.dir(dir); 215 cancelHelper.checkCancelled();
174 for (HgFileRevision fr : breakDown.get(dir)) { 216 return true;
175 visitor.file(fr); 217 } catch (HgCallbackTargetException ex) {
176 } 218 recordFailure(ex);
177 } 219 return false;
178 manifestContent.clear(); 220 } catch (CancelledException ex) {
179 } 221 recordCancel(ex);
180 visitor.end(manifestNodeid); 222 return false;
181 manifestNodeid = null; 223 } finally {
182 return true; 224 manifestNodeid = null;
225 }
183 } 226 }
184 227
185 public boolean next(Nodeid nid, Path fname, Flags flags) { 228 public boolean next(Nodeid nid, Path fname, Flags flags) {
186 if (matcher != null && !matcher.accept(fname)) { 229 if (matcher != null && !matcher.accept(fname)) {
187 return true; 230 return true;
188 } 231 }
189 HgFileRevision fr = new HgFileRevision(repo, nid, flags, fname); 232 try {
190 if (needDirs) { 233 HgFileRevision fr = new HgFileRevision(repo, nid, flags, fname);
191 manifestContent.add(fr); 234 if (needDirs) {
192 } else { 235 manifestContent.add(fr);
193 visitor.file(fr); 236 } else {
194 } 237 visitor.file(fr);
195 return true; 238 }
239 return true;
240 } catch (HgCallbackTargetException ex) {
241 recordFailure(ex);
242 return false;
243 }
196 } 244 }
197 } 245 }
198 } 246 }