--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2012 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.modeling.ui.diagram.monitor;\r
+\r
+import java.util.Map;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.concurrent.atomic.AtomicReference;\r
+\r
+import org.simantics.db.Disposable;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.procedure.Listener;\r
+import org.simantics.diagram.elements.MonitorClass;\r
+import org.simantics.g2d.canvas.Hints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class MonitorListener implements Listener<MonitorVariableValue>, Runnable, Disposable {\r
+\r
+ private final Resource element;\r
+ private ICanvasContext canvas;\r
+ private IDiagram diagram;\r
+ private final Map<String, String> substitutions;\r
+\r
+ private transient AtomicReference<MonitorVariableValue> lastScheduledUpdate = null;\r
+ private transient AtomicBoolean inUpdate = null;\r
+\r
+ public MonitorListener(Resource element, ICanvasContext canvas, IDiagram diagram, Map<String, String> substitutions) {\r
+ if (element == null)\r
+ throw new NullPointerException("null element");\r
+ if (canvas == null)\r
+ throw new NullPointerException("null canvas");\r
+ if (diagram == null)\r
+ throw new NullPointerException("null diagram");\r
+ if (substitutions == null)\r
+ throw new NullPointerException("null substitutions");\r
+ this.element = element;\r
+ this.canvas = canvas;\r
+ this.diagram = diagram;\r
+ this.substitutions = substitutions;\r
+ }\r
+\r
+ @Override\r
+ public void dispose() {\r
+ canvas = null;\r
+ diagram = null;\r
+ }\r
+\r
+ @Override\r
+ public void execute(MonitorVariableValue result) {\r
+ // Implement some kind of throttling for AWT thread element\r
+ // update scheduling to keep the amount of AWT scheduling\r
+ // down to a minimum.\r
+ if (inUpdate == null)\r
+ inUpdate = new AtomicBoolean(false);\r
+ if (lastScheduledUpdate == null)\r
+ lastScheduledUpdate = new AtomicReference<MonitorVariableValue>();\r
+\r
+ lastScheduledUpdate.set(result);\r
+\r
+ // Don't schedule update if there was already one in the pipe.\r
+ synchronized (inUpdate) {\r
+ if (!inUpdate.compareAndSet(false, true))\r
+ return;\r
+ }\r
+\r
+ //System.out.println(this + ".execute(" + result + "+")");\r
+ scheduleUpdate();\r
+ }\r
+\r
+ @Override\r
+ public void run() {\r
+ if (isDisposed())\r
+ return;\r
+\r
+ IElement el = ElementUtils.getByData(diagram, element);\r
+ if (el == null)\r
+ return;\r
+\r
+ try {\r
+ performUpdate(el);\r
+ } finally {\r
+ // Mark null to allow new update scheduling to commence.\r
+ synchronized (inUpdate) {\r
+ if (lastScheduledUpdate.get() != null)\r
+ scheduleUpdate();\r
+ else\r
+ inUpdate.set(false);\r
+ }\r
+ }\r
+ }\r
+\r
+ private void scheduleUpdate() {\r
+ ICanvasContext canvas = this.canvas;\r
+ if (!isDisposed())\r
+ ThreadUtils.asyncExec(canvas.getThreadAccess(), this);\r
+ }\r
+\r
+ private void performUpdate(IElement el) {\r
+ // Get the last updated monitor value but don't yet\r
+ // mark the container null to keep the outer code\r
+ // from scheduling new updates until this one is\r
+ // finished.\r
+ MonitorVariableValue result = lastScheduledUpdate.getAndSet(null);\r
+\r
+ String value = "<no variable>";\r
+ if (result != null) {\r
+ if (result.getValue() != null) {\r
+ value = result.getValue();//ValueFormatUtil.valueStr(result.getValue(), format);\r
+ } else {\r
+ value = "<no value>";\r
+ }\r
+ el.setHint(MonitorClass.KEY_MONITOR_COMPONENT, result.getMonitorVariable().getMonitorComponent());\r
+ ElementUtils.setOrRemoveHint(el, MonitorClass.KEY_MONITOR_IS_EXTERNAL, result.getMonitorVariable().isExternal());\r
+ } else {\r
+ el.removeHint(MonitorClass.KEY_MONITOR_COMPONENT);\r
+ el.removeHint(MonitorClass.KEY_MONITOR_IS_EXTERNAL);\r
+ }\r
+\r
+ substitutions.put("#v1", value);\r
+\r
+ final Map<String, String> subs = el.getHint(MonitorClass.KEY_MONITOR_SUBSTITUTIONS);\r
+ if (substitutions != subs)\r
+ el.setHint(MonitorClass.KEY_MONITOR_SUBSTITUTIONS, substitutions);\r
+\r
+ //System.out.println("REPLACING #v1: " + substitutions.get("#v1"));\r
+\r
+ el.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DELAYED_UPDATE);\r
+ }\r
+\r
+ @Override\r
+ public void exception(Throwable t) {\r
+ ErrorLogger.defaultLogError(t);\r
+ }\r
+\r
+ @Override\r
+ public boolean isDisposed() {\r
+ return canvas == null || diagram == null || canvas.isDisposed();\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return element.hashCode();\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ MonitorListener other = (MonitorListener) obj;\r
+ return element.equals(other.element);\r
+ }\r
+\r
+}
\ No newline at end of file