package de.lmu.ifi.dbs.elki.visualization.svg; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import de.lmu.ifi.dbs.elki.logging.LoggingUtil; /** * Class to handle updates to an SVG plot, in particular when used in an Apache * Batik UI. * * @author Erich Schubert * * @apiviz.has Runnable * @apiviz.uses UpdateSynchronizer */ public class UpdateRunner { /** * Owner/Synchronization object */ private Object sync; /** * The queue of pending updates */ final private Queue queue = new ConcurrentLinkedQueue<>(); /** * Synchronizer that can block events from being executed right away. */ private UpdateSynchronizer synchronizer = null; /** * Construct a new update handler * * @param sync Object to synchronize on */ protected UpdateRunner(Object sync) { this.sync = sync; } /** * Add a new update to run at any appropriate time. * * @param r New runnable to perform the update */ public void invokeLater(Runnable r) { queue.add(r); if(synchronizer == null) { runQueue(); } else { synchronizer.activate(); } } /** * Run the processing queue now. This should usually be only invoked by the * UpdateSynchronizer */ public void runQueue() { synchronized(sync) { while(!queue.isEmpty()) { Runnable r = queue.poll(); if(r != null) { try { r.run(); } catch(Exception e) { // Alternatively, we could allow the specification of exception // handlers for each runnable in the API. For now we'll just log. // TODO: handle exceptions here better! LoggingUtil.exception(e); } } else { LoggingUtil.warning("Tried to run a 'null' Object."); } } } } /** * Clear queue. For shutdown! */ public synchronized void clear() { queue.clear(); } /** * Check whether the queue is empty. * * @return queue status */ public boolean isEmpty() { return queue.isEmpty(); } /** * Set a new update synchronizer. * * @param newsync Update synchronizer */ public synchronized void synchronizeWith(UpdateSynchronizer newsync) { // LoggingUtil.warning("Synchronizing: " + sync + " " + newsync); if(synchronizer == newsync) { LoggingUtil.warning("Double-synced to the same plot!"); return; } if(synchronizer != null) { LoggingUtil.warning("Attempting to synchronize to more than one synchronizer."); return; } synchronizer = newsync; newsync.addUpdateRunner(this); } /** * Remove an update synchronizer * * @param oldsync Update synchronizer to remove */ public synchronized void unsynchronizeWith(UpdateSynchronizer oldsync) { if(synchronizer == null) { LoggingUtil.warning("Warning: was not synchronized."); } else { if(synchronizer != oldsync) { LoggingUtil.warning("Warning: was synchronized differently!"); return; } } // LoggingUtil.warning("Unsynchronizing: " + sync + " " + oldsync); synchronizer = null; runQueue(); } }