comparison src/org/tmatesoft/hg/core/HgStatusCommand.java @ 423:9c9c442b5f2e

Major refactoring of exception handling. Low-level API uses RuntimeExceptions, while checked are left for higher level
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 23 Mar 2012 22:51:18 +0100
parents 7f136a3fa671
children 6437d261048a
comparison
equal deleted inserted replaced
422:5d1cc7366d04 423:9c9c442b5f2e
20 import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex; 20 import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
21 import static org.tmatesoft.hg.repo.HgRepository.*; 21 import static org.tmatesoft.hg.repo.HgRepository.*;
22 22
23 import java.io.IOException; 23 import java.io.IOException;
24 import java.util.ConcurrentModificationException; 24 import java.util.ConcurrentModificationException;
25 import java.util.concurrent.CancellationException;
26 25
27 import org.tmatesoft.hg.internal.ChangelogHelper; 26 import org.tmatesoft.hg.internal.ChangelogHelper;
28 import org.tmatesoft.hg.repo.HgRepository; 27 import org.tmatesoft.hg.repo.HgRepository;
29 import org.tmatesoft.hg.repo.HgStatusCollector; 28 import org.tmatesoft.hg.repo.HgStatusCollector;
30 import org.tmatesoft.hg.repo.HgStatusInspector; 29 import org.tmatesoft.hg.repo.HgStatusInspector;
31 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; 30 import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector;
31 import org.tmatesoft.hg.util.CancelSupport;
32 import org.tmatesoft.hg.util.CancelledException;
32 import org.tmatesoft.hg.util.Path; 33 import org.tmatesoft.hg.util.Path;
33 import org.tmatesoft.hg.util.Status; 34 import org.tmatesoft.hg.util.Status;
34 35
35 /** 36 /**
36 * Command to obtain file status information, 'hg status' counterpart. 37 * Command to obtain file status information, 'hg status' counterpart.
159 160
160 /** 161 /**
161 * Perform status operation according to parameters set. 162 * Perform status operation according to parameters set.
162 * 163 *
163 * @param statusHandler callback to get status information 164 * @param statusHandler callback to get status information
164 * @throws IOException if there are (further unspecified) errors while walking working copy 165 * @throws HgCallbackTargetException wrapper for any exception user callback code may produce
166 * @throws CancelledException if execution of the command was cancelled
167 * @throws IOException FIXME EXCEPTIONS WTF it's doing here if there are (further unspecified) errors while walking working copy
165 * @throws IllegalArgumentException if handler is <code>null</code> 168 * @throws IllegalArgumentException if handler is <code>null</code>
166 * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread) 169 * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread)
167 */ 170 */
168 public void execute(HgStatusHandler statusHandler) throws CancellationException, HgException, IOException { 171 public void execute(HgStatusHandler statusHandler) throws HgCallbackTargetException, CancelledException, HgException, IOException {
169 if (statusHandler == null) { 172 if (statusHandler == null) {
170 throw new IllegalArgumentException(); 173 throw new IllegalArgumentException();
171 } 174 }
172 if (mediator.busy()) { 175 if (mediator.busy()) {
173 throw new ConcurrentModificationException(); 176 throw new ConcurrentModificationException();
175 HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext 178 HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext
176 // PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext 179 // PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext
177 try { 180 try {
178 // XXX if I need a rough estimation (for ProgressMonitor) of number of work units, 181 // XXX if I need a rough estimation (for ProgressMonitor) of number of work units,
179 // I may use number of files in either rev1 or rev2 manifest edition 182 // I may use number of files in either rev1 or rev2 manifest edition
180 mediator.start(statusHandler, new ChangelogHelper(repo, startRevision)); 183 mediator.start(statusHandler, getCancelSupport(statusHandler, true), new ChangelogHelper(repo, startRevision));
181 if (endRevision == WORKING_COPY) { 184 if (endRevision == WORKING_COPY) {
182 HgWorkingCopyStatusCollector wcsc = scope != null ? HgWorkingCopyStatusCollector.create(repo, scope) : new HgWorkingCopyStatusCollector(repo); 185 HgWorkingCopyStatusCollector wcsc = scope != null ? HgWorkingCopyStatusCollector.create(repo, scope) : new HgWorkingCopyStatusCollector(repo);
183 wcsc.setBaseRevisionCollector(sc); 186 wcsc.setBaseRevisionCollector(sc);
184 wcsc.walk(startRevision, mediator); 187 wcsc.walk(startRevision, mediator);
185 } else { 188 } else {
188 sc.change(endRevision, mediator); 191 sc.change(endRevision, mediator);
189 } else { 192 } else {
190 sc.walk(startRevision, endRevision, mediator); 193 sc.walk(startRevision, endRevision, mediator);
191 } 194 }
192 } 195 }
193 } catch (HgCallbackTargetException.Wrap ex) { 196 } catch (CancelledException ex) {
194 // seems too general to catch RuntimeException, i.e. 197 // this is our exception, thrown from Mediator.
195 // 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) 198 // next check shall throw original cause of the stop - either HgCallbackTargetException or original CancelledException
196 // XXX Perhaps, need more detailed explanation in handlers that are expected to throw Wrap/RTE (i.e. HgChangesetHandler) 199 mediator.checkFailure();
197 throw new HgCallbackTargetException(ex).setRevisionIndex(endRevision);
198 } finally { 200 } finally {
199 mediator.done(); 201 mediator.done();
200 } 202 }
201 } 203 }
202 204
206 @Deprecated 208 @Deprecated
207 public interface Handler extends HgStatusHandler{ 209 public interface Handler extends HgStatusHandler{
208 void handleStatus(HgStatus s); 210 void handleStatus(HgStatus s);
209 } 211 }
210 212
211 private class Mediator implements HgStatusInspector { 213 private class Mediator implements HgStatusInspector, CancelSupport {
212 boolean needModified; 214 boolean needModified;
213 boolean needAdded; 215 boolean needAdded;
214 boolean needRemoved; 216 boolean needRemoved;
215 boolean needUnknown; 217 boolean needUnknown;
216 boolean needMissing; 218 boolean needMissing;
217 boolean needClean; 219 boolean needClean;
218 boolean needIgnored; 220 boolean needIgnored;
219 boolean needCopies; 221 boolean needCopies;
220 HgStatusHandler handler; 222 HgStatusHandler handler;
221 private ChangelogHelper logHelper; 223 private ChangelogHelper logHelper;
224 private CancelSupport handlerCancelSupport;
225 private HgCallbackTargetException failure;
226 private CancelledException cancellation;
222 227
223 Mediator() { 228 Mediator() {
224 } 229 }
225 230
226 public void start(HgStatusHandler h, ChangelogHelper changelogHelper) { 231 public void start(HgStatusHandler h, CancelSupport hcs, ChangelogHelper changelogHelper) {
227 handler = h; 232 handler = h;
233 handlerCancelSupport = hcs;
228 logHelper = changelogHelper; 234 logHelper = changelogHelper;
229 } 235 }
230 236
231 public void done() { 237 public void done() {
232 handler = null; 238 handler = null;
239 handlerCancelSupport = null;
233 logHelper = null; 240 logHelper = null;
241 failure = null;
242 cancellation = null;
234 } 243 }
235 244
236 public boolean busy() { 245 public boolean busy() {
237 return handler != null; 246 return handler != null;
238 } 247 }
239 248
249 // XXX copy from ChangesetTransformer. Perhaps, can share the code?
250 public void checkFailure() throws HgCallbackTargetException, CancelledException {
251 // do not forget to clear exceptions for reuse of this instance
252 if (failure != null) {
253 HgCallbackTargetException toThrow = failure;
254 failure = null;
255 throw toThrow;
256 }
257 if (cancellation != null) {
258 CancelledException toThrow = cancellation;
259 cancellation = null;
260 throw toThrow;
261 }
262 }
263
264 // XXX copy from ChangesetTransformer. code sharing note above applies
265 public void checkCancelled() throws CancelledException {
266 if (failure != null || cancellation != null) {
267 // stop status iteration. Our exception is for the purposes of cancellation only,
268 // the one we have stored (this.cancellation) is for user
269 throw new CancelledException();
270 }
271 }
272
273 private void dispatch(HgStatus s) {
274 try {
275 handler.handleStatus(s);
276 handlerCancelSupport.checkCancelled();
277 } catch (HgCallbackTargetException ex) {
278 failure = ex;
279 } catch (CancelledException ex) {
280 cancellation = ex;
281 }
282 }
283
240 public void modified(Path fname) { 284 public void modified(Path fname) {
241 if (needModified) { 285 if (needModified) {
242 handler.handleStatus(new HgStatus(Modified, fname, logHelper)); 286 dispatch(new HgStatus(Modified, fname, logHelper));
243 } 287 }
244 } 288 }
245 public void added(Path fname) { 289 public void added(Path fname) {
246 if (needAdded) { 290 if (needAdded) {
247 handler.handleStatus(new HgStatus(Added, fname, logHelper)); 291 dispatch(new HgStatus(Added, fname, logHelper));
248 } 292 }
249 } 293 }
250 public void removed(Path fname) { 294 public void removed(Path fname) {
251 if (needRemoved) { 295 if (needRemoved) {
252 handler.handleStatus(new HgStatus(Removed, fname, logHelper)); 296 dispatch(new HgStatus(Removed, fname, logHelper));
253 } 297 }
254 } 298 }
255 public void copied(Path fnameOrigin, Path fnameAdded) { 299 public void copied(Path fnameOrigin, Path fnameAdded) {
256 if (needCopies) { 300 if (needCopies) {
257 // TODO post-1.0 in fact, merged files may report 'copied from' as well, correct status kind thus may differ from Added 301 // TODO post-1.0 in fact, merged files may report 'copied from' as well, correct status kind thus may differ from Added
258 handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); 302 dispatch(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper));
259 } 303 }
260 } 304 }
261 public void missing(Path fname) { 305 public void missing(Path fname) {
262 if (needMissing) { 306 if (needMissing) {
263 handler.handleStatus(new HgStatus(Missing, fname, logHelper)); 307 dispatch(new HgStatus(Missing, fname, logHelper));
264 } 308 }
265 } 309 }
266 public void unknown(Path fname) { 310 public void unknown(Path fname) {
267 if (needUnknown) { 311 if (needUnknown) {
268 handler.handleStatus(new HgStatus(Unknown, fname, logHelper)); 312 dispatch(new HgStatus(Unknown, fname, logHelper));
269 } 313 }
270 } 314 }
271 public void clean(Path fname) { 315 public void clean(Path fname) {
272 if (needClean) { 316 if (needClean) {
273 handler.handleStatus(new HgStatus(Clean, fname, logHelper)); 317 dispatch(new HgStatus(Clean, fname, logHelper));
274 } 318 }
275 } 319 }
276 public void ignored(Path fname) { 320 public void ignored(Path fname) {
277 if (needIgnored) { 321 if (needIgnored) {
278 handler.handleStatus(new HgStatus(Ignored, fname, logHelper)); 322 dispatch(new HgStatus(Ignored, fname, logHelper));
279 } 323 }
280 } 324 }
281 325
282 public void invalid(Path fname, Exception ex) { 326 public void invalid(Path fname, Exception err) {
283 handler.handleError(fname, new Status(Status.Kind.ERROR, "Failed to get file status", ex)); 327 try {
328 handler.handleError(fname, new Status(Status.Kind.ERROR, "Failed to get file status", err));
329 handlerCancelSupport.checkCancelled();
330 } catch (HgCallbackTargetException ex) {
331 failure = ex;
332 } catch (CancelledException ex) {
333 cancellation = ex;
334 }
284 } 335 }
285 } 336 }
286 } 337 }