summaryrefslogtreecommitdiff
path: root/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
diff options
context:
space:
mode:
Diffstat (limited to 'addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java')
-rw-r--r--addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java184
1 files changed, 184 insertions, 0 deletions
diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
new file mode 100644
index 00000000..6fe43df1
--- /dev/null
+++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
@@ -0,0 +1,184 @@
+package de.lmu.ifi.dbs.elki.visualization.batikutil;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2015
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.lang.ref.WeakReference;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.batik.bridge.UpdateManager;
+import org.apache.batik.bridge.UpdateManagerAdapter;
+import org.apache.batik.bridge.UpdateManagerEvent;
+import org.apache.batik.swing.svg.JSVGComponent;
+
+import de.lmu.ifi.dbs.elki.visualization.svg.UpdateRunner;
+import de.lmu.ifi.dbs.elki.visualization.svg.UpdateSynchronizer;
+
+/**
+ * This class is used to synchronize SVG updates with an JSVG canvas.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses UpdateRunner
+ */
+class JSVGUpdateSynchronizer implements UpdateSynchronizer {
+ /**
+ * A weak reference to the component the plot is in.
+ */
+ private final WeakReference<JSVGComponent> cref;
+
+ /**
+ * The UpdateRunner we are put into
+ */
+ private Set<WeakReference<UpdateRunner>> updaterunner = new CopyOnWriteArraySet<>();
+
+ /**
+ * Adapter to track component changes
+ */
+ private final UMAdapter umadapter = new UMAdapter();
+
+ /**
+ * The current Runnable scheduled, prevents repeated invocations.
+ */
+ private final AtomicReference<Runnable> pending = new AtomicReference<>();
+
+ /**
+ * Create an updateSynchronizer for the given component.
+ *
+ * @param component Component to manage updates on.
+ */
+ protected JSVGUpdateSynchronizer(JSVGComponent component) {
+ assert(component != null);
+
+ this.cref = new WeakReference<>(component);
+ // Hook into UpdateManager creation.
+ component.addUpdateManagerListener(umadapter);
+ // makeRunnerIfNeeded();
+ }
+
+ @Override
+ public void activate() {
+ makeRunnerIfNeeded();
+ }
+
+ /**
+ * Join the runnable queue of a component.
+ */
+ protected void makeRunnerIfNeeded() {
+ // We don't need to make a SVG runner when there are no pending updates.
+ boolean stop = true;
+ for(WeakReference<UpdateRunner> wur : updaterunner) {
+ UpdateRunner ur = wur.get();
+ if(ur == null) {
+ updaterunner.remove(wur);
+ }
+ else if(!ur.isEmpty()) {
+ stop = false;
+ }
+ }
+ if(stop) {
+ return;
+ }
+ // We only need a new runner when we don't have one in the queue yet!
+ if(pending.get() != null) {
+ return;
+ }
+ // We need a component
+ JSVGComponent component = this.cref.get();
+ if(component == null) {
+ return;
+ }
+ // Synchronize with all layers:
+ synchronized(this) {
+ synchronized(component) {
+ UpdateManager um = component.getUpdateManager();
+ if(um != null) {
+ synchronized(um) {
+ if(um.isRunning()) {
+ // Create and insert a runner.
+ Runnable newrunner = new Runnable() {
+ @Override
+ public void run() {
+ if(pending.compareAndSet(this, null)) {
+ // Wake up all runners
+ for(WeakReference<UpdateRunner> wur : updaterunner) {
+ UpdateRunner ur = wur.get();
+ if(ur == null || ur.isEmpty()) {
+ continue;
+ }
+ ur.runQueue();
+ }
+ }
+ }
+ };
+ pending.set(newrunner);
+ um.getUpdateRunnableQueue().invokeLater(newrunner);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void addUpdateRunner(UpdateRunner updateRunner) {
+ for(WeakReference<UpdateRunner> wur : updaterunner) {
+ if(wur.get() == null) {
+ updaterunner.remove(wur);
+ }
+ }
+ updaterunner.add(new WeakReference<>(updateRunner));
+ }
+
+ /**
+ * Adapter that will track the component for UpdateManager changes.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private class UMAdapter extends UpdateManagerAdapter {
+ /**
+ * Constructor. Protected to allow construction above.
+ */
+ protected UMAdapter() {
+ // nothing to do.
+ }
+
+ /**
+ * React to an update manager becoming available.
+ */
+ @Override
+ public void managerStarted(UpdateManagerEvent e) {
+ makeRunnerIfNeeded();
+ }
+
+ @Override
+ public void managerStopped(UpdateManagerEvent e) {
+ pending.set(null);
+ }
+ }
+}