package org.das2.util; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * TickleTimer is a timer that fires once it's been left alone for * a while. The idea is the keyboard can be pecked away and * the change event will not be fired until the keyboard is idle. * * This is a copy of the TickleTimer used in Autoplot. * * java.util.Timer performs very similar functionality. * @author Jeremy Faden */ public class TickleTimer { long tickleTime; long delay; boolean running; boolean firing=false; // true when the listener is being invoked. boolean retickle=false; // true when the tickleTimer was tickled while firing. List messages; List callbacks= new LinkedList<>(); static final Logger log= org.das2.util.LoggerManager.getLogger("das2.util"); /** * @param delay time in milliseconds to wait until firing off the change. * If delay is =<0, then events will be fired off immediately. * @param listener provides the callback when the timer * runs out. The listener is added as one of the bean's property * change listeners. */ public TickleTimer( long delay, PropertyChangeListener listener ) { this.tickleTime= System.currentTimeMillis(); this.delay= delay; addPropertyChangeListener( listener ); this.running= false; messages= new ArrayList<>(); } private void startTimer() { running= true; if ( delay<=0 ) { newRunnable().run(); } else { new Thread( newRunnable(), "tickleTimerThread" ).start(); messages.clear(); } } private Runnable newRunnable() { return new Runnable() { @Override public void run() { long d= System.currentTimeMillis() - tickleTime; while ( d < delay ) { try { log.log(Level.FINER, "tickleTimer sleep {0}", (delay - d)); Thread.sleep( delay-d ); } catch (InterruptedException ex) { throw new RuntimeException(ex); } d= System.currentTimeMillis() - tickleTime; } log.log(Level.FINER, "tickleTimer fire after {0}", d ); firing= true; running= false; //sometimes listeners need to retickle the timer... propertyChangeSupport.firePropertyChange("running",true,false); firing= false; if ( retickle ) { retickle= false; tickle("retickle"); } messages= new ArrayList<>(); List callbacks= new ArrayList<>(TickleTimer.this.callbacks); TickleTimer.this.callbacks.removeAll(callbacks); PropertyChangeEvent pce= new PropertyChangeEvent( this, "running", true, false ); callbacks.forEach((pcl) -> { try { pcl.propertyChange( pce ); } catch ( Exception ex ) { log.log( Level.WARNING, ex.getMessage(), ex ); } }); } }; } public synchronized void tickle(){ tickle(null); } public synchronized void tickle( String message ) { tickleTime= System.currentTimeMillis(); if ( firing ) { retickle= true; return; } if ( !running ) startTimer(); if ( message!=null ) messages.add(message); } public synchronized void tickle( String message, PropertyChangeListener callback ) { tickleTime= System.currentTimeMillis(); if ( firing ) { retickle= true; return; } if ( !running ) startTimer(); if ( message!=null ) messages.add(message); callbacks.add( callback ); } private final java.beans.PropertyChangeSupport propertyChangeSupport = new java.beans.PropertyChangeSupport(this); public final void addPropertyChangeListener(java.beans.PropertyChangeListener l) { propertyChangeSupport.addPropertyChangeListener(l); } public final void removePropertyChangeListener(java.beans.PropertyChangeListener l) { propertyChangeSupport.removePropertyChangeListener(l); } public boolean isRunning() { return this.running; } public void setRunning(boolean running) { boolean oldRunning = this.running; this.running = running; propertyChangeSupport.firePropertyChange ("running", Boolean.valueOf(oldRunning), Boolean.valueOf(running)); } public List getMessages() { return Collections.unmodifiableList(messages); } }