1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.scenegraph.profile.common;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.concurrent.TimeUnit;
21 import org.simantics.Simantics;
22 import org.simantics.db.AsyncRequestProcessor;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Session;
25 import org.simantics.db.common.session.SessionEventListenerAdapter;
26 import org.simantics.db.common.utils.Logger;
27 import org.simantics.db.procedure.Procedure;
28 import org.simantics.db.service.SessionEventSupport;
29 import org.simantics.scenegraph.INode;
30 import org.simantics.scenegraph.g2d.G2DSceneGraph;
31 import org.simantics.scenegraph.profile.EvaluationContext;
32 import org.simantics.scenegraph.profile.ProfileEntry;
33 import org.simantics.scenegraph.profile.Style;
34 import org.simantics.scenegraph.profile.impl.DebugPolicy;
35 import org.simantics.scenegraph.profile.impl.ProfileActivationListener;
36 import org.simantics.scenegraph.profile.request.RuntimeProfileActiveEntries;
37 import org.simantics.utils.datastructures.Pair;
38 import org.simantics.utils.datastructures.disposable.IDisposable;
39 import org.simantics.utils.threads.IThreadWorkQueue;
40 import org.simantics.utils.threads.ThreadUtils;
42 public class ProfileObserver implements EvaluationContext {
44 private final Session session;
47 * Runtime diagram resource.
49 private final Resource resource;
51 private final IDisposable canvas;
52 private final IThreadWorkQueue thread;
53 @SuppressWarnings("unused")
54 private final Object diagram;
55 private final Runnable notification;
56 private final G2DSceneGraph sceneGraph;
58 private volatile boolean dirty = true;
59 private volatile boolean disposed = false;
61 private List<Pair<Style, Object>> updates = new ArrayList<>();
62 private boolean updateAll;
64 private ProfileActivationListener activationListener;
66 private Map<String, Object> constants = new HashMap<String, Object>();
68 private Map<INode, Map<String, Object>> temporaryProperties = new HashMap<INode, Map<String, Object>>();
69 private Map<INode, Map<String, Object>> properties = new HashMap<INode, Map<String, Object>>();
71 private final SessionEventListenerAdapter transactionListener = new SessionEventListenerAdapter() {
73 public void writeTransactionFinished() {
80 public void readTransactionFinished() {
88 public ProfileObserver(Session session, Resource resource, IThreadWorkQueue thread, IDisposable canvas, G2DSceneGraph sceneGraph, Object diagram, Map<String, Object> constants, Runnable notification) {
89 //System.out.println(this + " NEW PROFILE OBSERVER: ");
90 this.session = session;
91 this.resource = resource;
94 this.diagram = diagram;
95 this.sceneGraph = sceneGraph;
96 this.constants.putAll(constants);
97 this.notification = notification;
99 attachSessionListener();
101 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
102 System.out.println("ProfileObserver(" + this + ")");
104 // Tell SceneGraph that this observer is not yet done applying its operations
105 if(sceneGraph != null)
106 sceneGraph.setPending(ProfileObserver.this);
110 private void attachSessionListener() {
111 SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
112 eventSupport.addListener(transactionListener);
115 private void detachSessionListener() {
116 SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
117 eventSupport.removeListener(transactionListener);
120 public void dispose() {
121 synchronized (this) {
127 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
128 System.out.println("ProfileObserver.dispose(" + this + ")");
130 if(activationListener != null) {
131 activationListener.cleanup();
132 activationListener = null;
135 detachSessionListener();
139 public void update(Style style, Object item) {
140 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
141 System.out.println("Profile observer marked dirty.");
143 updates.add(Pair.make(style, item));
148 public void update() {
153 private void perform() {
155 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
156 System.out.println("Profile observer detected a change.");
158 session.asyncRequest(new RuntimeProfileActiveEntries(resource), new Procedure<Collection<ProfileEntry>>() {
160 public void execute(final Collection<ProfileEntry> entries) {
165 ThreadUtils.asyncExec(thread, new Runnable() {
171 temporaryProperties.clear();
173 long t0 = DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM ? System.nanoTime() : 0L;
176 for(ProfileEntry e : entries) {
177 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
178 System.out.println("Apply profile entry: " + e);
179 e.apply(ProfileObserver.this);
182 // FIXME: Updates which were applied should be cleared to prevent double update.
183 // We can't, however, clear all updates since some nodes might still be missing.
186 List<Pair<Style, Object>> updatesCopy = new ArrayList<>(updates);
188 for (Pair<Style, Object> update : updatesCopy) {
189 Style style = update.first;
190 Object item = update.second;
192 style.apply2(item, ProfileObserver.this);
196 if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM) {
197 long t1 = System.nanoTime();
198 System.out.println((t1-t0) / 1e6);
202 sceneGraph.setPending(ProfileObserver.this);
203 // System.err.println("setPending, dirty=true");
206 sceneGraph.clearPending(ProfileObserver.this);
207 // System.err.println("clearPending, dirty=false");
211 // canvas.getContentContext().setDirty();
213 // Something is underway, schedule update
215 Simantics.async(() -> {
216 if (isDisposed()) return;
217 if (dirty) perform();
218 }, 100, TimeUnit.MILLISECONDS);
225 public void exception(Throwable t) {
226 Logger.defaultLogError(t);
232 public boolean isDisposed() {
233 return disposed || canvas.isDisposed();
237 public void exception(Throwable throwable) {
238 Logger.defaultLogError(throwable);
241 @SuppressWarnings("unchecked")
243 public <T> T getTemporaryProperty(INode element, String key) {
244 Map<String, Object> map = temporaryProperties.get(element);
245 T t = map == null ? null : (T) map.get(key);
246 //System.out.println(this + ".getTemporaryProperty(" + element + ", " + key + "): " + t);
251 public <T> void setTemporaryProperty(INode element, String key, T value) {
252 //System.out.println(this + ".setTemporaryProperty(" + element + ", " + key + ", " + value + ")");
253 Map<String, Object> map = temporaryProperties.get(element);
257 map = new HashMap<String, Object>(8);
258 temporaryProperties.put(element, map);
263 temporaryProperties.remove(element);
268 @SuppressWarnings("unchecked")
270 public <T> T getProperty(INode element, String key) {
271 Map<String, Object> map = properties.get(element);
272 T t = map == null ? null : (T) map.get(key);
273 //System.out.println(this + ".getProperty(" + element + ", " + key + "): " + t);
277 @SuppressWarnings("unchecked")
279 public <T> T setProperty(INode element, String key, T value) {
281 //System.out.println(this + ".setProperty(" + element + ", " + key + ", " + value + ")");
282 Map<String, Object> map = properties.get(element);
286 map = new HashMap<String, Object>(8);
287 properties.put(element, map);
290 result = (T) map.remove(key);
292 properties.remove(element);
294 result = (T) map.put(key, value);
298 @SuppressWarnings("unchecked")
300 public <T> T getConstant(String key) {
301 return (T) constants.get(key);
305 public String toString() {
306 return "ProfileObserver[" + resource.getResourceId() + "]";
310 // public ICanvasContext getContext() {
315 // public IDiagram getDiagram() {
319 public void listen(AsyncRequestProcessor processor, IDisposable disposable) {
320 activationListener = new ProfileActivationListener(resource, this, disposable);
321 processor.asyncRequest(new RuntimeProfileActiveEntries(resource), activationListener);
325 public Resource getResource() {
330 public G2DSceneGraph getSceneGraph() {