--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in 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.scenegraph.profile.common;\r
+\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.db.AsyncRequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.common.session.SessionEventListenerAdapter;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.procedure.Procedure;\r
+import org.simantics.db.service.SessionEventSupport;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.g2d.G2DSceneGraph;\r
+import org.simantics.scenegraph.profile.EvaluationContext;\r
+import org.simantics.scenegraph.profile.ProfileEntry;\r
+import org.simantics.scenegraph.profile.impl.DebugPolicy;\r
+import org.simantics.scenegraph.profile.impl.ProfileActivationListener;\r
+import org.simantics.scenegraph.profile.request.RuntimeProfileActiveEntries;\r
+import org.simantics.utils.datastructures.disposable.IDisposable;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+public class ProfileObserver implements EvaluationContext {\r
+\r
+ private final Session session;\r
+\r
+ /**\r
+ * Runtime diagram resource.\r
+ */\r
+ private final Resource resource;\r
+\r
+ private final IDisposable canvas;\r
+ private final IThreadWorkQueue thread;\r
+ @SuppressWarnings("unused")\r
+ private final Object diagram;\r
+ private final Runnable notification;\r
+ private final G2DSceneGraph sceneGraph;\r
+\r
+ private boolean dirty = true;\r
+ private boolean disposed = false;\r
+\r
+ private ProfileActivationListener activationListener;\r
+\r
+ private Map<String, Object> constants = new HashMap<String, Object>();\r
+\r
+ private Map<INode, Map<String, Object>> temporaryProperties = new HashMap<INode, Map<String, Object>>();\r
+ private Map<INode, Map<String, Object>> properties = new HashMap<INode, Map<String, Object>>();\r
+\r
+ private final SessionEventListenerAdapter transactionListener = new SessionEventListenerAdapter() {\r
+ @Override\r
+ public void writeTransactionFinished() {\r
+ if (isDisposed())\r
+ dispose();\r
+ if (dirty)\r
+ perform();\r
+ }\r
+ @Override\r
+ public void readTransactionFinished() {\r
+ if (isDisposed())\r
+ dispose();\r
+ if (dirty)\r
+ perform();\r
+ }\r
+ };\r
+\r
+ public ProfileObserver(Session session, Resource resource, IThreadWorkQueue thread, IDisposable canvas, G2DSceneGraph sceneGraph, Object diagram, Map<String, Object> constants, Runnable notification) {\r
+ //System.out.println(this + " NEW PROFILE OBSERVER: ");\r
+ this.session = session;\r
+ this.resource = resource;\r
+ this.thread = thread;\r
+ this.canvas = canvas;\r
+ this.diagram = diagram;\r
+ this.sceneGraph = sceneGraph;\r
+ this.constants.putAll(constants);\r
+ this.notification = notification;\r
+\r
+ attachSessionListener();\r
+\r
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)\r
+ System.out.println("ProfileObserver(" + this + ")");\r
+ \r
+ // Tell SceneGraph that this observer is not yet done applying its operations\r
+ if(sceneGraph != null)\r
+ sceneGraph.setPending(ProfileObserver.this);\r
+ \r
+ }\r
+\r
+ private void attachSessionListener() {\r
+ SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);\r
+ eventSupport.addListener(transactionListener);\r
+ }\r
+\r
+ private void detachSessionListener() {\r
+ SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);\r
+ eventSupport.removeListener(transactionListener);\r
+ }\r
+\r
+ public void dispose() {\r
+ synchronized (this) {\r
+ if (disposed)\r
+ return;\r
+ disposed = true;\r
+ }\r
+\r
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)\r
+ System.out.println("ProfileObserver.dispose(" + this + ")");\r
+\r
+ if(activationListener != null) { \r
+ activationListener.cleanup();\r
+ activationListener = null;\r
+ }\r
+\r
+ detachSessionListener();\r
+ }\r
+\r
+ @Override\r
+ public void update() {\r
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)\r
+ System.out.println("Profile observer marked dirty.");\r
+ dirty = true;\r
+ }\r
+\r
+ private void perform() {\r
+ dirty = false;\r
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)\r
+ System.out.println("Profile observer detected a change.");\r
+ \r
+ session.asyncRequest(new RuntimeProfileActiveEntries(resource), new Procedure<Collection<ProfileEntry>>() {\r
+ @Override\r
+ public void execute(final Collection<ProfileEntry> entries) {\r
+\r
+ if (isDisposed())\r
+ return;\r
+\r
+ ThreadUtils.asyncExec(thread, new Runnable() {\r
+ \r
+// private void init(INode node) {\r
+// //ProfileVariables.init(node, ProfileObserver.this);\r
+//// NodeUtil.forChildren(node, new NodeProcedure<Object>() {\r
+//// @Override\r
+//// public Object execute(INode node, String id) {\r
+//// init(node);\r
+//// return null;\r
+//// }\r
+//// }, null);\r
+// }\r
+\r
+ @Override\r
+ public void run() {\r
+\r
+ if (isDisposed())\r
+ return;\r
+\r
+ temporaryProperties.clear();\r
+\r
+// init(sceneGraph);\r
+\r
+// for(IElement e : diagram.getElements()) {\r
+// Node node = NodeUtils.\r
+// Variables.init(e, ProfileObserver.this);\r
+// }\r
+\r
+ for(ProfileEntry e : entries) {\r
+ if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)\r
+ System.out.println("Apply profile entry: " + e);\r
+ e.apply(ProfileObserver.this);\r
+ }\r
+\r
+ if(dirty) {\r
+ sceneGraph.setPending(ProfileObserver.this);\r
+// System.err.println("setPending, dirty=true");\r
+ }\r
+ else {\r
+ sceneGraph.clearPending(ProfileObserver.this);\r
+// System.err.println("clearPending, dirty=false");\r
+ }\r
+ \r
+ notification.run();\r
+// canvas.getContentContext().setDirty();\r
+ \r
+ // Something is underway, schedule update\r
+ if(dirty) {\r
+ Simantics.async(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+\r
+ if (isDisposed()) return;\r
+ \r
+ if(dirty) perform();\r
+ \r
+ }\r
+ \r
+ }, 100, TimeUnit.MILLISECONDS);\r
+ }\r
+\r
+ }\r
+ });\r
+ }\r
+\r
+ @Override\r
+ public void exception(Throwable t) {\r
+ Logger.defaultLogError(t);\r
+ }\r
+ });\r
+ }\r
+\r
+ @Override\r
+ public boolean isDisposed() {\r
+ return disposed || canvas.isDisposed();\r
+ }\r
+\r
+ @Override\r
+ public void exception(Throwable throwable) {\r
+ Logger.defaultLogError(throwable);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getTemporaryProperty(INode element, String key) {\r
+ Map<String, Object> map = temporaryProperties.get(element);\r
+ T t = map == null ? null : (T) map.get(key);\r
+ //System.out.println(this + ".getTemporaryProperty(" + element + ", " + key + "): " + t);\r
+ return t;\r
+ }\r
+\r
+ @Override\r
+ public <T> void setTemporaryProperty(INode element, String key, T value) {\r
+ //System.out.println(this + ".setTemporaryProperty(" + element + ", " + key + ", " + value + ")");\r
+ Map<String, Object> map = temporaryProperties.get(element);\r
+ if (map == null) {\r
+ if (value == null)\r
+ return;\r
+ map = new HashMap<String, Object>(8);\r
+ temporaryProperties.put(element, map);\r
+ }\r
+ if (value == null) {\r
+ map.remove(key);\r
+ if (map.isEmpty())\r
+ properties.remove(element);\r
+ } else\r
+ map.put(key, value);\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getProperty(INode element, String key) {\r
+ Map<String, Object> map = properties.get(element);\r
+ T t = map == null ? null : (T) map.get(key);\r
+ //System.out.println(this + ".getProperty(" + element + ", " + key + "): " + t);\r
+ return t;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T setProperty(INode element, String key, T value) {\r
+ T result = null;\r
+ //System.out.println(this + ".setProperty(" + element + ", " + key + ", " + value + ")");\r
+ Map<String, Object> map = properties.get(element);\r
+ if (map == null) {\r
+ if (value == null)\r
+ return null;\r
+ map = new HashMap<String, Object>(8);\r
+ properties.put(element, map);\r
+ }\r
+ if (value == null) {\r
+ result = (T) map.remove(key);\r
+ if (map.isEmpty())\r
+ properties.remove(element);\r
+ } else\r
+ result = (T) map.put(key, value);\r
+ return result;\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T> T getConstant(String key) {\r
+ return (T) constants.get(key);\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "ProfileObserver[" + resource.getResourceId() + "]";\r
+ }\r
+\r
+// @Override\r
+// public ICanvasContext getContext() {\r
+// return canvas;\r
+// }\r
+// \r
+// @Override\r
+// public IDiagram getDiagram() {\r
+// return diagram;\r
+// }\r
+\r
+ public void listen(AsyncRequestProcessor processor, IDisposable disposable) {\r
+ activationListener = new ProfileActivationListener(resource, this, disposable);\r
+ processor.asyncRequest(new RuntimeProfileActiveEntries(resource), activationListener);\r
+ }\r
+\r
+ @Override\r
+ public Resource getResource() {\r
+ return resource;\r
+ }\r
+\r
+ @Override\r
+ public G2DSceneGraph getSceneGraph() {\r
+ return sceneGraph;\r
+ }\r
+\r
+}\r