tikhomirov@215: /* tikhomirov@394: * Copyright (c) 2011-2012 TMate Software Ltd tikhomirov@215: * tikhomirov@215: * This program is free software; you can redistribute it and/or modify tikhomirov@215: * it under the terms of the GNU General Public License as published by tikhomirov@215: * the Free Software Foundation; version 2 of the License. tikhomirov@215: * tikhomirov@215: * This program is distributed in the hope that it will be useful, tikhomirov@215: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@215: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@215: * GNU General Public License for more details. tikhomirov@215: * tikhomirov@215: * For information on how to redistribute this software under tikhomirov@215: * the terms of a license other than GNU General Public License tikhomirov@215: * contact TMate Software at support@hg4j.com tikhomirov@215: */ tikhomirov@215: package org.tmatesoft.hg.core; tikhomirov@215: tikhomirov@275: import org.tmatesoft.hg.util.Path; tikhomirov@215: tikhomirov@275: tikhomirov@215: tikhomirov@215: /** tikhomirov@370: * Checked exception that indicates errors in client code and tries to supply extra information about the context it occurred in. tikhomirov@370: * tikhomirov@370: * Generally, client need to pass own error information/exceptions from within implementations of the callback methods they supply. tikhomirov@370: * However, there's no straightforward way to alter throws clause for these methods, and alternatives like generic {@link Exception} or tikhomirov@370: * library's own {@link HgException} are rather obscure. Suggested approach is to wrap whatever exception user code produces with tikhomirov@370: * {@link RuntimeException} subclass, {@link Wrap}. Then, unwrap and re-throw with checked {@link HgCallbackTargetException}. tikhomirov@370: * tikhomirov@370: * FIXME REVISIT perhaps, shall just throw HgCallbackTargetException from any handler, and do not catch anything in commands at all? tikhomirov@394: * FIXME decide whether shall root at HgException ("throws HgException, HgCallbackTargetException" looks a bit odd now) tikhomirov@215: * tikhomirov@215: * @author Artem Tikhomirov tikhomirov@215: * @author TMate Software Ltd. tikhomirov@215: */ tikhomirov@215: @SuppressWarnings("serial") tikhomirov@215: public class HgCallbackTargetException extends HgException { tikhomirov@215: /** tikhomirov@215: * @param cause can't be null tikhomirov@215: */ tikhomirov@215: public HgCallbackTargetException(Throwable cause) { tikhomirov@216: super((String) null); tikhomirov@215: if (cause == null) { tikhomirov@215: throw new IllegalArgumentException(); tikhomirov@215: } tikhomirov@215: if (cause.getClass() == Wrap.class) { tikhomirov@215: // eliminate wrapper tikhomirov@215: initCause(cause.getCause()); tikhomirov@215: } else { tikhomirov@215: initCause(cause); tikhomirov@215: } tikhomirov@215: } tikhomirov@215: tikhomirov@215: @SuppressWarnings("unchecked") tikhomirov@215: public T getTargetException() { tikhomirov@215: return (T) getCause(); tikhomirov@215: } tikhomirov@215: tikhomirov@215: /** tikhomirov@215: * Despite this exception is merely a way to give users access to their own exceptions, it may still supply tikhomirov@215: * valuable debugging information about what led to the error. tikhomirov@215: */ tikhomirov@215: @Override tikhomirov@215: public String getMessage() { tikhomirov@215: StringBuilder sb = new StringBuilder(); tikhomirov@320: sb.append("Original exception thrown: "); tikhomirov@320: sb.append(getCause().getClass().getName()); tikhomirov@320: sb.append(" at "); tikhomirov@275: appendDetails(sb); tikhomirov@215: return sb.toString(); tikhomirov@215: } tikhomirov@215: tikhomirov@275: @Override tikhomirov@275: public HgCallbackTargetException setRevision(Nodeid r) { tikhomirov@275: return (HgCallbackTargetException) super.setRevision(r); tikhomirov@275: } tikhomirov@275: @Override tikhomirov@367: public HgCallbackTargetException setRevisionIndex(int rev) { tikhomirov@367: return (HgCallbackTargetException) super.setRevisionIndex(rev); tikhomirov@275: } tikhomirov@275: @Override tikhomirov@275: public HgCallbackTargetException setFileName(Path name) { tikhomirov@275: return (HgCallbackTargetException) super.setFileName(name); tikhomirov@275: } tikhomirov@275: tikhomirov@215: /** tikhomirov@215: * Given the approach high-level handlers throw RuntimeExceptions to indicate errors, and tikhomirov@215: * a need to throw reasonable checked exception from client code, clients may utilize this class tikhomirov@215: * to get their checked exceptions unwrapped by {@link HgCallbackTargetException} and serve as that tikhomirov@215: * exception cause, eliminating {@link RuntimeException} mediator. tikhomirov@215: */ tikhomirov@215: public static final class Wrap extends RuntimeException { tikhomirov@215: tikhomirov@215: public Wrap(Throwable cause) { tikhomirov@215: super(cause); tikhomirov@215: if (cause == null) { tikhomirov@215: throw new IllegalArgumentException(); tikhomirov@215: } tikhomirov@215: } tikhomirov@215: } tikhomirov@215: }