/* * Copyright 2004 - 2008 Christian Sprajc. All rights reserved. * * This file is part of PowerFolder. * * PowerFolder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * PowerFolder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PowerFolder. If not, see <http://www.gnu.org/licenses/>. * * $Id: UploadsQuickInfoPanel.java 4504 2008-07-02 13:30:59Z harry $ */ package de.dal33t.powerfolder.ui.util; import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import de.dal33t.powerfolder.Controller; import de.dal33t.powerfolder.util.Reject; /** * Helper class to perform UI updates delayed. If an UI update is scheduled * twice quickly after each other (<delay) only last update is actually * performed. But it is ensured that at least every "delay" time period at least * an update is performed once. This is useful if you want to reduce the number * of events in EDT thread. ATTENTION: Do only schedule events in ONE * DelayedUpdater that can override each other. This utility class DISCARDS * previously scheduled events. In other words: The last update wins and gets * executed only. * <p> * Graphcial representation. E = scheduled event, U = update in UI. * <p> * Timeline with one event: * <p> * -----E-----------U----------------- * <p> * Two events: * <p> * -----E-------E---U----------------- * <p> * Many events: * <p> * -----E--E-E--E---U--E--E----E---U-- * * @author Christian Sprajc * @version $Revision$ */ public class DelayedUpdater { /* Not static because log access is marshaled by synchronized call. */ private final Logger log = Logger.getLogger(DelayedUpdater.class.getName()); private static final long DEFAULT_DELAY = 250L; private static final int NOT_SCHEDULED = -1; private long delay; private long nextMandatoryEvent = NOT_SCHEDULED; private final ScheduledExecutorService executorService; private volatile DelayedTimerTask currentTask; /** * Constructs a delayed execution in 250ms. Uses shared timer from Controller. * * @param controller */ public DelayedUpdater(Controller controller) { this(controller, DEFAULT_DELAY); } /** * Constructs a delayed execution. Uses shared timer from Controller. * * @param controller * @param delay * the delay to use */ public DelayedUpdater(Controller controller, long delay) { executorService = controller.getThreadPool(); this.delay = delay; } public synchronized void setDelay(long delay) { Reject.ifTrue(delay < 0, "Illegal delay value: " + delay); this.delay = delay; } public long getDelay() { return delay; } /** * Schedules a task to be performed in EDT thread after a given time. If a * former update has not been processed yet the old task gets canceled. * * @param task */ public synchronized void schedule(Runnable task) { if (currentTask != null) { currentTask.cancel(); currentTask.canceled = true; } currentTask = new DelayedTimerTask(task); try { long now = System.currentTimeMillis(); if (nextMandatoryEvent == NOT_SCHEDULED) { nextMandatoryEvent = now + delay; } long delayUntilEvent = Math.max(nextMandatoryEvent - now, 0); executorService.schedule(currentTask, delayUntilEvent, TimeUnit.MILLISECONDS); } catch (Exception e) { log.log(Level.FINER, "Unable to schedule task to timer: " + e, e); } } private class DelayedTimerTask extends TimerTask { private final Runnable task; private volatile boolean canceled; private DelayedTimerTask(Runnable task) { this.task = task; canceled = false; } @Override public void run() { // Ready for new tasks synchronized (DelayedUpdater.this) { currentTask = null; nextMandatoryEvent = NOT_SCHEDULED; if (canceled) { return; } } UIUtil.invokeLaterInEDT(new Runnable() { public void run() { if (canceled) { return; } try { task.run(); } catch (Exception e) { log.log(Level.SEVERE, "Exception while executing delayed task: " + e, e); } } }); } } }