]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ProfileObserver.java
Workaround to profile updating problem
[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                             // 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.
184                             //updates.clear(); 
185                         } else {
186                             List<Pair<Style, Object>> updatesCopy = new ArrayList<>(updates);
187                             updates.clear();
188                             for (Pair<Style, Object> update : updatesCopy) {
189                                 Style style = update.first;
190                                 Object item = update.second;
191                                 
192                                 style.apply2(item, ProfileObserver.this);
193                             }
194                         }
195
196                         if (DebugPolicy.DEBUG_PROFILE_OBSERVER_PERFORM) {
197                             long t1 = System.nanoTime();
198                             System.out.println((t1-t0) / 1e6);
199                         }
200                         
201                         if(dirty) {
202                                 sceneGraph.setPending(ProfileObserver.this);
203 //                              System.err.println("setPending, dirty=true");
204                         }
205                         else {
206                                 sceneGraph.clearPending(ProfileObserver.this);
207 //                              System.err.println("clearPending, dirty=false");
208                         }
209                         
210                         notification.run();
211 //                        canvas.getContentContext().setDirty();
212                         
213                         // Something is underway, schedule update
214                         if (dirty) {
215                             Simantics.async(() -> {
216                                 if (isDisposed()) return;
217                                 if (dirty) perform();
218                             }, 100, TimeUnit.MILLISECONDS);
219                         }
220                     }
221                 });
222             }
223
224             @Override
225             public void exception(Throwable t) {
226                 Logger.defaultLogError(t);
227             }
228         });
229     }
230
231     @Override
232     public boolean isDisposed() {
233         return disposed || canvas.isDisposed();
234     }
235
236     @Override
237     public void exception(Throwable throwable) {
238         Logger.defaultLogError(throwable);
239     }
240
241     @SuppressWarnings("unchecked")
242     @Override
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);
247         return t;
248     }
249
250     @Override
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);
254         if (map == null) {
255             if (value == null)
256                 return;
257             map = new HashMap<String, Object>(8);
258             temporaryProperties.put(element, map);
259         }
260         if (value == null) {
261             map.remove(key);
262             if (map.isEmpty())
263                 temporaryProperties.remove(element);
264         } else
265             map.put(key, value);
266     }
267
268     @SuppressWarnings("unchecked")
269     @Override
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);
274         return t;
275     }
276
277     @SuppressWarnings("unchecked")
278     @Override
279     public <T> T setProperty(INode element, String key, T value) {
280         T result = null;
281         //System.out.println(this + ".setProperty(" + element + ", " + key + ", " + value + ")");
282         Map<String, Object> map = properties.get(element);
283         if (map == null) {
284             if (value == null)
285                 return null;
286             map = new HashMap<String, Object>(8);
287             properties.put(element, map);
288         }
289         if (value == null) {
290             result = (T) map.remove(key);
291             if (map.isEmpty())
292                 properties.remove(element);
293         } else
294             result = (T) map.put(key, value);
295         return result;
296     }
297
298     @SuppressWarnings("unchecked")
299     @Override
300     public <T> T getConstant(String key) {
301         return (T) constants.get(key);
302     }
303
304     @Override
305     public String toString() {
306         return "ProfileObserver[" + resource.getResourceId() + "]";
307     }
308
309 //    @Override
310 //    public ICanvasContext getContext() {
311 //      return canvas;
312 //    }
313 //    
314 //    @Override
315 //    public IDiagram getDiagram() {
316 //      return diagram;
317 //    }
318
319     public void listen(AsyncRequestProcessor processor, IDisposable disposable) {
320         activationListener = new ProfileActivationListener(resource, this, disposable);
321         processor.asyncRequest(new RuntimeProfileActiveEntries(resource), activationListener);
322     }
323
324     @Override
325     public Resource getResource() {
326         return resource;
327     }
328
329     @Override
330     public G2DSceneGraph getSceneGraph() {
331         return sceneGraph;
332     }
333
334 }