/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yixia.zi.widget.cropimage;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
/**
* An abstract class for the interface <code>Cancelable</code>. Subclass can
* simply override the <code>execute()</code> function to provide an
* implementation of <code>Cancelable</code>.
*/
public abstract class BaseCancelable<T> implements Cancelable<T> {
/**
* The state of the task, possible transitions are:
*
* <pre>
* INITIAL -> CANCELED
* EXECUTING -> COMPLETE, CANCELING, ERROR, CANCELED
* CANCELING -> CANCELED
* </pre>
*
* When the task stop, it must be end with one of the following states:
* COMPLETE, CANCELED, or ERROR;
*/
private static final int STATE_INITIAL = (1 << 0);
private static final int STATE_EXECUTING = (1 << 1);
private static final int STATE_CANCELING = (1 << 2);
private static final int STATE_CANCELED = (1 << 3);
private static final int STATE_ERROR = (1 << 4);
private static final int STATE_COMPLETE = (1 << 5);
private int mState = STATE_INITIAL;
private Throwable mError;
private T mResult;
private Cancelable<?> mCurrentTask;
private Thread mThread;
protected abstract T execute() throws Exception;
protected synchronized void interruptNow() {
if (isInStates(STATE_CANCELING | STATE_EXECUTING)) {
mThread.interrupt();
}
}
/**
* Frees the result (which is not null) when the task has been canceled.
*/
protected void freeCanceledResult(T result) {
// Do nothing by default;
}
private boolean isInStates(int states) {
return (states & mState) != 0;
}
private T handleTerminalStates() throws ExecutionException {
if (mState == STATE_CANCELED) {
throw new CancellationException();
}
if (mState == STATE_ERROR) {
throw new ExecutionException(mError);
}
if (mState == STATE_COMPLETE)
return mResult;
throw new IllegalStateException();
}
public synchronized void await() throws InterruptedException {
while (!isInStates(STATE_COMPLETE | STATE_CANCELED | STATE_ERROR)) {
wait();
}
}
public final T get() throws InterruptedException, ExecutionException {
synchronized (this) {
if (mState != STATE_INITIAL) {
await();
return handleTerminalStates();
}
mThread = Thread.currentThread();
mState = STATE_EXECUTING;
}
try {
mResult = execute();
} catch (CancellationException e) {
mState = STATE_CANCELED;
} catch (InterruptedException e) {
mState = STATE_CANCELED;
} catch (Throwable error) {
synchronized (this) {
if (mState != STATE_CANCELING) {
mError = error;
mState = STATE_ERROR;
}
}
}
synchronized (this) {
if (mState == STATE_CANCELING)
mState = STATE_CANCELED;
if (mState == STATE_EXECUTING)
mState = STATE_COMPLETE;
notifyAll();
if (mState == STATE_CANCELED && mResult != null) {
freeCanceledResult(mResult);
}
return handleTerminalStates();
}
}
/**
* Requests the task to be canceled.
*
* @return true if the task is running and has not been canceled; false
* otherwise
*/
public synchronized boolean requestCancel() {
if (mState == STATE_INITIAL) {
mState = STATE_CANCELED;
notifyAll();
return false;
}
if (mState == STATE_EXECUTING) {
if (mCurrentTask != null)
mCurrentTask.requestCancel();
mState = STATE_CANCELING;
return true;
}
return false;
}
/**
* Whether the task's has been requested for cancel.
*/
protected synchronized boolean isCanceling() {
return mState == STATE_CANCELING;
}
/**
* Runs a <code>Cancelable</code> subtask. This method is helpful, if the
* task can be composed of several cancelable tasks. By using this function,
* it will pass <code>requestCancel</code> message to those subtasks.
*
* @param <T> the return type of the sub task
* @param cancelable the sub task
* @return the result of the subtask
*/
@SuppressWarnings("hiding")
protected <T> T runSubTask(Cancelable<T> cancelable) throws InterruptedException, ExecutionException {
synchronized (this) {
if (mCurrentTask != null) {
throw new IllegalStateException("cannot two subtasks at the same time");
}
if (mState == STATE_CANCELING)
throw new CancellationException();
mCurrentTask = cancelable;
}
try {
return cancelable.get();
} finally {
synchronized (this) {
mCurrentTask = null;
}
}
}
}