package roboguice.util;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import android.os.Handler;
import android.os.Looper;
// TODO
// - has no execute() method
// - has no progress support
// - has no onInterrupted support
// - add retry support
public class AndroidCallableWrapper<ResultT> implements Runnable {
protected Handler handler;
protected AndroidCallableI<ResultT> delegate;
protected StackTraceElement[] launchLocation;
@SuppressWarnings("MALICIOUS_CODE")
public AndroidCallableWrapper(Handler handler, AndroidCallableI<ResultT> delegate, StackTraceElement[] launchLocation ) {
this.delegate = delegate;
this.launchLocation = launchLocation;
this.handler = handler != null ? handler : new Handler(Looper.getMainLooper());
}
@java.lang.SuppressWarnings("unchecked")
@Override
public void run() {
ResultT result = null;
Exception exception = null;
try {
if (isPreCallOverriden((Class<? extends AndroidCallableI<?>>) delegate.getClass()))
beforeCall();
result = doDoInBackgroundThread();
} catch (Exception e) {
exception = e;
} finally {
afterCall(result, exception);
}
}
void beforeCall() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final Exception[] exceptions = new Exception[1];
// Execute onSuccess in the UI thread, but wait
// for it to complete.
// If it throws an exception, capture that exception
// and rethrow it later.
handler.post(new Runnable() {
public void run() {
try {
new Callable<Object>() {
@Override
public Object call() throws Exception {
doOnPreCall();
return null;
}
}.call();
} catch (Exception e) {
exceptions[0] = e;
} finally {
latch.countDown();
}
}
});
// Wait for onSuccess to finish
latch.await();
if (exceptions[0] != null)
throw exceptions[0];
}
void afterCall(final ResultT result, final Exception e) {
handler.post(new Runnable() {
@Override
public void run() {
try {
if (e != null) {
if( launchLocation!=null ) {
final StackTraceElement[] stackTrace = e.getStackTrace();
final StackTraceElement[] result = new StackTraceElement[stackTrace.length + launchLocation.length];
System.arraycopy(stackTrace, 0, result, 0, stackTrace.length);
System.arraycopy(launchLocation, 0, result, stackTrace.length, launchLocation.length);
e.setStackTrace(result);
}
doOnException(e);
} else {
doOnSuccess(result);
}
} finally {
doOnFinally();
}
}
});
}
protected void doOnPreCall() throws Exception {
delegate.onPreCall();
}
protected ResultT doDoInBackgroundThread() throws Exception {
return delegate.doInBackground();
}
protected void doOnSuccess(ResultT result) {
delegate.onSuccess(result);
}
protected void doOnException(Exception e) {
delegate.onException(e);
}
protected void doOnFinally() {
delegate.onFinally();
}
static HashMap<Class<? extends AndroidCallableI<?>>, Boolean> isPreCallOverriddenMap = new HashMap<Class<? extends AndroidCallableI<?>>, Boolean>();
static boolean isPreCallOverriden(Class<? extends AndroidCallableI<?>> subClass) {
try {
Boolean tmp = isPreCallOverriddenMap.get(subClass);
if (tmp != null)
return tmp;
tmp = subClass.getMethod("onPreCall").getDeclaringClass() != AndroidCallableWrapper.class;
isPreCallOverriddenMap.put(subClass, tmp);
return tmp;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}