]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ProfileObserver.java
9d379c890b9bd952a3ac2b6db941b3c71acc07c6
[simantics/platform.git] / bundles / org.simantics.scenegraph.profile / src / org / simantics / scenegraph / profile / common / ProfileObserver.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.scenegraph.profile.common;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.concurrent.TimeUnit;
20
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;
41
42 public class ProfileObserver implements EvaluationContext {
43
44     private final Session                     session;
45
46     /**
47      * Runtime diagram resource.
48      */
49     private final Resource                    resource;
50
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;
57
58     private volatile boolean                  dirty               = true;
59     private volatile boolean                  disposed            = false;
60
61     private List<Pair<Style, Object>>         updates             = new ArrayList<>();
62     private boolean                           updateAll;
63     
64     private ProfileActivationListener         activationListener;
65
66     private Map<String, Object>               constants           = new HashMap<String, Object>();
67
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>>();
70
71     private final SessionEventListenerAdapter transactionListener = new SessionEventListenerAdapter() {
72         @Override
73         public void writeTransactionFinished() {
74             if (isDisposed())
75                 dispose();
76             if (dirty)
77                 perform();
78         }
79         @Override
80         public void readTransactionFinished() {
81             if (isDisposed())
82                 dispose();
83             if (dirty)
84                 perform();
85         }
86     };
87
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;
92         this.thread = thread;
93         this.canvas = canvas;
94         this.diagram = diagram;
95         this.sceneGraph = sceneGraph;
96         this.constants.putAll(constants);
97         this.notification = notification;
98
99         attachSessionListener();
100
101         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
102             System.out.println("ProfileObserver(" + this + ")");
103         
104         // Tell SceneGraph that this observer is not yet done applying its operations
105         if(sceneGraph != null)
106             sceneGraph.setPending(ProfileObserver.this);
107         
108     }
109
110     private void attachSessionListener() {
111         SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
112         eventSupport.addListener(transactionListener);
113     }
114
115     private void detachSessionListener() {
116         SessionEventSupport eventSupport = session.getService(SessionEventSupport.class);
117         eventSupport.removeListener(transactionListener);
118     }
119
120     public void dispose() {
121         synchronized (this) {
122             if (disposed)
123                 return;
124             disposed = true;
125         }
126
127         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM)
128             System.out.println("ProfileObserver.dispose(" + this + ")");
129
130         if(activationListener != null) { 
131             activationListener.cleanup();
132             activationListener = null;
133         }
134
135         detachSessionListener();
136     }
137
138     @Override
139     public void update(Style style, Object item) {
140         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
141             System.out.println("Profile observer marked dirty.");
142         
143         updates.add(Pair.make(style, item));
144         //updateAll = true;
145         dirty = true;
146     }
147
148     public void update() {
149         updateAll = true;
150         dirty = true;
151     }
152     
153     private void perform() {
154         dirty = false;
155         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_UPDATE)
156             System.out.println("Profile observer detected a change.");
157         
158         session.asyncRequest(new RuntimeProfileActiveEntries(resource), new Procedure<Collection<ProfileEntry>>() {
159             @Override
160             public void execute(final Collection<ProfileEntry> entries) {
161
162                 if (isDisposed())
163                     return;
164
165                 ThreadUtils.asyncExec(thread, new Runnable() {
166                     @Override
167                     public void run() {
168                         if (isDisposed())
169                             return;
170
171                         temporaryProperties.clear();
172
173                         long t0 = DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM ? System.nanoTime() : 0L;
174                         
175                         if (updateAll) {
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);
180                             }
181                             updateAll = false;
182                             updates.clear();
183                         } else {
184                             List<Pair<Style, Object>> updatesCopy = new ArrayList<>(updates);
185                             updates.clear();
186                             for (Pair<Style, Object> update : updatesCopy) {
187                                 Style style = update.first;
188                                 Object item = update.second;
189                                 
190                                 style.apply2(item, ProfileObserver.this);
191                             }
192                         }
193
194                         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM) {
195                             long t1 = System.nanoTime();
196                             System.out.println((t1-t0) / 1e6);
197                         }
198                         
199                         if(dirty) {
200                                 sceneGraph.setPending(ProfileObserver.this);
201 //                              System.err.println("setPending, dirty=true");
202                         }
203                         else {
204                                 sceneGraph.clearPending(ProfileObserver.this);
205 //                              System.err.println("clearPending, dirty=false");
206                         }
207                         
208                         notification.run();
209 //                        canvas.getContentContext().setDirty();
210                         
211                         // Something is underway, schedule update
212                         if (dirty) {
213                             Simantics.async(() -> {
214                                 if (isDisposed()) return;
215                                 if (dirty) perform();
216                             }, 100, TimeUnit.MILLISECONDS);
217                         }
218                     }
219                 });
220             }
221
222             @Override
223             public void exception(Throwable t) {
224                 Logger.defaultLogError(t);
225             }
226         });
227     }
228
229     @Override
230     public boolean isDisposed() {
231         return disposed || canvas.isDisposed();
232     }
233
234     @Override
235     public void exception(Throwable throwable) {
236         Logger.defaultLogError(throwable);
237     }
238
239     @SuppressWarnings("unchecked")
240     @Override
241     public <T> T getTemporaryProperty(INode element, String key) {
242         Map<String, Object> map = temporaryProperties.get(element);
243         T t = map == null ? null : (T) map.get(key);
244         //System.out.println(this + ".getTemporaryProperty(" + element + ", " + key + "): " + t);
245         return t;
246     }
247
248     @Override
249     public <T> void setTemporaryProperty(INode element, String key, T value) {
250         //System.out.println(this + ".setTemporaryProperty(" + element + ", " + key + ", " + value + ")");
251         Map<String, Object> map = temporaryProperties.get(element);
252         if (map == null) {
253             if (value == null)
254                 return;
255             map = new HashMap<String, Object>(8);
256             temporaryProperties.put(element, map);
257         }
258         if (value == null) {
259             map.remove(key);
260             if (map.isEmpty())
261                 temporaryProperties.remove(element);
262         } else
263             map.put(key, value);
264     }
265
266     @SuppressWarnings("unchecked")
267     @Override
268     public <T> T getProperty(INode element, String key) {
269         Map<String, Object> map = properties.get(element);
270         T t = map == null ? null : (T) map.get(key);
271         //System.out.println(this + ".getProperty(" + element + ", " + key + "): " + t);
272         return t;
273     }
274
275     @SuppressWarnings("unchecked")
276     @Override
277     public <T> T setProperty(INode element, String key, T value) {
278         T result = null;
279         //System.out.println(this + ".setProperty(" + element + ", " + key + ", " + value + ")");
280         Map<String, Object> map = properties.get(element);
281         if (map == null) {
282             if (value == null)
283                 return null;
284             map = new HashMap<String, Object>(8);
285             properties.put(element, map);
286         }
287         if (value == null) {
288             result = (T) map.remove(key);
289             if (map.isEmpty())
290                 properties.remove(element);
291         } else
292             result = (T) map.put(key, value);
293         return result;
294     }
295
296     @SuppressWarnings("unchecked")
297     @Override
298     public <T> T getConstant(String key) {
299         return (T) constants.get(key);
300     }
301
302     @Override
303     public String toString() {
304         return "ProfileObserver[" + resource.getResourceId() + "]";
305     }
306
307 //    @Override
308 //    public ICanvasContext getContext() {
309 //      return canvas;
310 //    }
311 //    
312 //    @Override
313 //    public IDiagram getDiagram() {
314 //      return diagram;
315 //    }
316
317     public void listen(AsyncRequestProcessor processor, IDisposable disposable) {
318         activationListener = new ProfileActivationListener(resource, this, disposable);
319         processor.asyncRequest(new RuntimeProfileActiveEntries(resource), activationListener);
320     }
321
322     @Override
323     public Resource getResource() {
324         return resource;
325     }
326
327     @Override
328     public G2DSceneGraph getSceneGraph() {
329         return sceneGraph;
330     }
331
332 }