/**
* Copyright 2010 Google Inc.
*
* 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 org.waveprotocol.wave.client.scheduler;
import com.google.common.annotations.VisibleForTesting;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.client.impl.SchedulerImpl;
/**
* A command that is idempotentally schedulable and cancellable.
*
* This class primarily exists to work around the absence of isScheduled() and
* cancel() methods in GWT's scheduler interface.
*
*/
public abstract class IdempotentFinally implements ScheduledCommand {
private enum State {
/** Not scheduled. */
NONE,
/** Scheduled, and will run doExecute() when invoked. */
SCHEDULED_TO_RUN,
/** Scheduled, but will do nothing when invoked. */
SCHEDULED_TO_SKIP
}
private final Scheduler scheduler;
private State state = State.NONE;
@VisibleForTesting
IdempotentFinally(Scheduler scheduler) {
this.scheduler = scheduler;
}
protected IdempotentFinally() {
this(SchedulerImpl.get());
}
/**
* Schedules this command to run before the end of this event loop. If it is
* already scheduled to run, does nothing. If it was previously cancelled, it
* is uncancelled.
*/
public final void schedule() {
switch(state) {
case NONE:
scheduler.scheduleFinally(this);
state = State.SCHEDULED_TO_RUN;
break;
case SCHEDULED_TO_SKIP:
// Already scheduled.
state = State.SCHEDULED_TO_RUN;
break;
case SCHEDULED_TO_RUN:
// Do nothing.
break;
}
assert state == State.SCHEDULED_TO_RUN;
}
/**
* Cancels this command from running, if it is currently scheduled.
*/
public final void cancel() {
switch(state) {
case NONE:
// Do nothing.
break;
case SCHEDULED_TO_SKIP:
// Do nothing.
break;
case SCHEDULED_TO_RUN:
// Already scheduled.
state = State.SCHEDULED_TO_SKIP;
break;
}
assert state != State.SCHEDULED_TO_RUN;
}
@Override
public final void execute() {
switch (state) {
case NONE:
throw new IllegalStateException( "IdempotentFinally run when not scheduled");
case SCHEDULED_TO_SKIP:
state = State.NONE;
// Skip doExecute();
break;
case SCHEDULED_TO_RUN:
state = State.NONE;
doExecute();
break;
}
}
/**
* Runs this command. Subclasses must override this to provide their command
* logic.
*/
protected abstract void doExecute();
}