blob: b1fa84dab17a2cf2aadd6db0f8f4371770b1ebfa [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.util.ui;
import com.intellij.openapi.Disposable;
import com.intellij.util.ConcurrencyUtil;
import org.jetbrains.annotations.NonNls;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public abstract class Timer implements Disposable, Runnable {
private final int mySpan;
private final String myName;
private volatile boolean myTakeInitialDelay = true;
private Exception myInterruptedException;
private final Object LOCK = new Object();
private SharedThread mySharedThread;
private ScheduledFuture<?> myFuture;
enum TimerState {startup, intialSleep, running, suspended, restarting, pausing, disposed}
private TimerState myState = TimerState.startup;
private int myPauseTime;
public Timer(@NonNls String name, int span) {
myName = name;
mySpan = span;
mySharedThread = SharedThread.getInstance();
}
public void setTakeInitialDelay(final boolean take) {
myTakeInitialDelay = take;
}
public final int getSpan() {
return mySpan;
}
public final void start() {
synchronized (LOCK) {
if (isRunning() || isDisposed()) return;
myState = TimerState.startup;
mySharedThread.queue(this, 0);
}
}
public void run() {
synchronized (LOCK) {
switch (myState) {
case startup:
startup();
break;
case restarting:
startup();
break;
case intialSleep:
myState = TimerState.running;
fireAndReschedule();
break;
case running:
fireAndReschedule();
break;
case suspended:
break;
case pausing:
myState = TimerState.running;
mySharedThread.queue(this, myPauseTime);
break;
case disposed:
break;
}
}
}
private void startup() {
myState = TimerState.intialSleep;
if (myTakeInitialDelay) {
mySharedThread.queue(this, mySpan);
} else {
fireAndReschedule();
}
}
private void fireAndReschedule() {
try {
onTimer();
}
catch (InterruptedException e) {
myInterruptedException = e;
suspend();
return;
}
mySharedThread.queue(this, getSpan());
}
protected abstract void onTimer() throws InterruptedException;
public final void suspend() {
synchronized (LOCK) {
if (isDisposed() || !isRunning()) return;
myState = TimerState.suspended;
}
}
public final void delay(int length) {
synchronized (LOCK) {
if (isDisposed() || !isRunning()) return;
myState = TimerState.pausing;
mySharedThread.queue(this, length);
}
}
public final void resume() {
synchronized (LOCK) {
if (isDisposed() || isRunning()) return;
myState = TimerState.running;
mySharedThread.queue(this, 0);
}
}
public final void dispose() {
synchronized (LOCK) {
myState = TimerState.disposed;
}
}
public void restart() {
synchronized (LOCK) {
myState = TimerState.restarting;
mySharedThread.queue(this, 0);
}
}
public boolean isRunning() {
synchronized (LOCK) {
return myState == TimerState.running || myState == TimerState.intialSleep || myState == TimerState.restarting;
}
}
public boolean isDisposed() {
synchronized (LOCK) {
return myState == TimerState.disposed;
}
}
@SuppressWarnings({"HardCodedStringLiteral"})
public String toString() {
return "Timer=" + myName;
}
private static class SharedThread {
private final ScheduledThreadPoolExecutor myExecutor;
private static SharedThread ourInstance;
private SharedThread() {
myExecutor = ConcurrencyUtil.newSingleScheduledThreadExecutor("AnimatorThread");
}
public static SharedThread getInstance() {
if (ourInstance == null) {
ourInstance = new SharedThread();
}
return ourInstance;
}
public void queue(Timer timer, int span) {
final ScheduledFuture<?> future = timer.getFuture();
if (future != null) {
future.cancel(true);
}
timer.setFuture(myExecutor.schedule(timer, span, TimeUnit.MILLISECONDS));
}
}
private void setFuture(ScheduledFuture<?> schedule) {
myFuture = schedule;
}
public ScheduledFuture<?> getFuture() {
return myFuture;
}
}