]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/profile/StyleBase.java
Fix warnings from newly added code
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / profile / StyleBase.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management in
3  * 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.diagram.profile;
13
14 import java.util.Arrays;
15
16 import org.simantics.databoard.Bindings;
17 import org.simantics.db.ReadGraph;
18 import org.simantics.db.RequestProcessor;
19 import org.simantics.db.Resource;
20 import org.simantics.db.Session;
21 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
22 import org.simantics.db.common.request.TernaryRead;
23 import org.simantics.db.common.request.UnaryRead;
24 import org.simantics.db.common.utils.NameUtils;
25 import org.simantics.db.exception.DatabaseException;
26 import org.simantics.db.layer0.variable.Variable;
27 import org.simantics.db.procedure.Listener;
28 import org.simantics.db.request.Read;
29 import org.simantics.diagram.stubs.DiagramResource;
30 import org.simantics.g2d.canvas.ICanvasContext;
31 import org.simantics.g2d.diagram.IDiagram;
32 import org.simantics.g2d.element.IElement;
33 import org.simantics.scenegraph.INode;
34 import org.simantics.scenegraph.profile.DataNodeMap;
35 import org.simantics.scenegraph.profile.EvaluationContext;
36 import org.simantics.scenegraph.profile.Group;
37 import org.simantics.scenegraph.profile.Observer;
38 import org.simantics.scenegraph.profile.Style;
39 import org.simantics.scenegraph.profile.common.ObserverGroupListener;
40 import org.simantics.scenegraph.profile.common.ObserverGroupValueListener;
41 import org.simantics.scenegraph.profile.impl.DebugPolicy;
42 import org.simantics.scl.runtime.tuple.Tuple3;
43 import org.simantics.utils.datastructures.Pair;
44
45 /**
46  * For most style implementations it should be enough to override the following
47  * methods:
48  * <ul>
49  * <li>{@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}</li>
50  * <li>{@link #applyStyleForElement(Observer, Object, IElement, Object)}</li>
51  * <li>{@link #cleanupStyleForElement(IDiagram, IElement)}</li>
52  * </ul>
53  * 
54  * <p>
55  * See each method for a specification of what they are intended for.
56  * 
57  * Optionally you can also override
58  * {@link #styleResultChanged(Observer, Resource, Object)} for optimization
59  * purposes but this is usually not necessary.
60  * 
61  * @author Tuukka Lehtonen
62  * f
63  * @param <Result> type of result objects for styled group items tracked by this
64  *        style implementation, see
65  *        {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}
66  */
67 public abstract class StyleBase<Result> implements Style {
68
69     private Object identity;
70
71     public StyleBase(Object identity) {
72         this.identity = identity;
73     }
74
75     public StyleBase() {
76         this.identity = getClass();
77     }
78
79     @SuppressWarnings("unchecked")
80     protected <T> T getIdentity() {
81         return (T)identity;
82     }
83
84     @Override
85     public int hashCode() {
86         final int prime = 31;
87         int result = 1;
88         result = prime * result + ((identity == null) ? 0 : identity.hashCode());
89         return result;
90     }
91
92     @Override
93     public boolean equals(Object obj) {
94         if (this == obj)
95             return true;
96         if (obj == null)
97             return false;
98         if (getClass() != obj.getClass())
99             return false;
100         StyleBase<?> other = (StyleBase<?>) obj;
101         if (identity == null) {
102             if (other.identity != null)
103                 return false;
104         } else if (!identity.equals(other.identity))
105             return false;
106         return true;
107     }
108
109     protected Resource getResource() {
110         return getIdentity();
111     }
112
113     /**
114      * For caching this simple base request that is done in every
115      * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource)}.
116      */
117     static class RuntimeDiagramVariableRequest extends UnaryRead<Resource, Variable> {
118         public RuntimeDiagramVariableRequest(Resource runtimeDiagram) {
119             super(runtimeDiagram);
120         }
121
122         @Override
123         public Variable perform(ReadGraph graph) throws DatabaseException {
124             DiagramResource DIA = DiagramResource.getInstance(graph);
125             String variableURI = graph.getPossibleRelatedValue(parameter, DIA.RuntimeDiagram_HasVariable, Bindings.STRING);
126             if (variableURI == null)
127                 return null;
128             Variable activeVariable = org.simantics.db.layer0.variable.Variables.getPossibleVariable(graph, variableURI);
129             return activeVariable;
130         }
131     }
132
133     /**
134      * Calculates a typed result to be used for applying the style to a diagram
135      * in
136      * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}
137      * . The graph request system will take care of only notifying other systems
138      * when the style result actually changes.
139      * 
140      * <p>
141      * This implementation uses {@link RuntimeDiagramVariableRequest} to
142      * discover the active composite variable related to the specified
143      * runtimeDiagram and invokes
144      * {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
145      * with it.
146      * 
147      * @param graph
148      *            database read access
149      * @param runtimeDiagram
150      *            resource describing the runtime data of the styled diagram
151      * @param entry
152      *            profile entry resource, don't care if now aware
153      * @param groupItem
154      *            an item belonging to the observed group
155      * @return the calculated style result object
156      * @throws DatabaseException
157      * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
158      */
159     public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException {
160         Variable activeVariable = graph.syncRequest(new RuntimeDiagramVariableRequest(runtimeDiagram), TransientCacheListener.<Variable>instance());
161         if (activeVariable == null)
162             return null;
163
164         //System.out.println("URI1: " + configuration.getURI(graph));
165         //System.out.println("URI2: " + activeVariable.getURI(graph));
166         return calculateStyle(graph, runtimeDiagram, entry, groupItem, activeVariable);
167     }
168
169     /**
170      * Calculates a typed result to be used for applying the style to a diagram
171      * in
172      * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}
173      * . The graph request system will take care of only notifying other systems
174      * when the style result actually changes.
175      * 
176      * @param graph database read access
177      * @param runtimeDiagram resource describing the runtime data of the styled
178      *        diagram
179      * @param entry profile entry resource, don't care if now aware
180      * @param groupItem an item belonging to the observed group
181      * @param activeComposite variable for accessing the active realization of the
182      *        diagram's corresponding composite. This may change when
183      *        experiments are activate and deactivated. When there is no
184      *        experiment active, this is the base realization, i.e. the
185      *        configuration.
186      * @return the calculated style result object
187      * @throws DatabaseException
188      * @see {@link StyleBase#calculateStyle(ReadGraph, Resource, Resource, Resource, Variable)}
189      */
190     public Result calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem, Variable activeComposite) throws DatabaseException {
191         return null;
192     }
193
194     /**
195      * Invoked when the result calculated by
196      * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} changes.
197      * Used for keeping track of the latest calculated style values that are
198      * applied in
199      * {@link #applyStyleForElement(Observer, IDiagram, Object, IElement, Object)}.
200      * 
201      * @param observer
202      * @param object
203      * @param result
204      */
205     public void styleResultChanged(Observer observer, Resource runtimeDiagram, Resource object, Result result) {
206         if (result == null)
207             StyleBaseData.getInstance().removeValue(new Tuple3(this, runtimeDiagram, object));
208         else
209             StyleBaseData.getInstance().putValue(new Tuple3(this, runtimeDiagram, object), result);
210         observer.update();
211     }
212
213     /**
214      * Apply the latest style result calculated by
215      * {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)} to the
216      * scene graph of the specified diagram item (i.e. element).
217      * 
218      * <p>
219      * <code>StyleBase</code> ensures that this method is invoked in the AWT
220      * thread only.
221      * 
222      * @param observer profile system observer
223      * @param item the styled diagram item data
224      * @param element the styled diagram element representing the data item
225      * @param result the latest result calculated by
226      *        {@link #calculateStyle(ReadGraph, Resource, Resource, Variable)}
227      */
228     public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Result result) {
229     }
230     
231     public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Result value) {
232         
233         final INode node = map.getNode(item);
234         if (node == null) {
235             evaluationContext.update();
236             // TODO: continue or return?
237             return;
238         }
239
240         if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)
241             System.out.println(StyleBase.this + ": applying style for item " + item + " and element " + node + " with result " + value);
242
243         applyStyleForNode(evaluationContext, node, value);
244         
245     }
246
247     /**
248      * This method is invoked by
249      * {@link #cleanupStyleForNode(EvaluationContext, INode)} when the style is
250      * deactivated. It is invoked for each diagram element tracked by the style
251      * before deactivation.
252      * 
253      * @param node a previously tracked and styled scene graph node
254      */
255     protected void cleanupStyleForNode(INode node) {
256     }
257
258     /**
259      * This method is invoked by
260      * {@link #cleanupStyleForItem(EvaluationContext, DataNodeMap, Object)} when the style is
261      * deactivated. It is invoked for each diagram element tracked by the style
262      * before deactivation.
263      * @param evaluationContext the context of this style evaluation
264      * @param node a previously tracked and styled scene graph node
265      */
266     protected void cleanupStyleForNode(EvaluationContext evaluationContext, INode node) {
267         cleanupStyleForNode(node);
268     }
269
270     protected void cleanupStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item) {
271
272         final INode node = map.getNode(item);
273         if (node != null) {
274             if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
275                 System.out.println(this + ".cleanupStyleForItem(" + item + " = " + node + ")");
276             cleanupStyleForNode(evaluationContext, node);
277         }
278
279     }
280     
281     
282     static class GroupListener<T> extends ObserverGroupListener {
283         
284         private StyleBase<T> style;
285         private Session session;
286         private Resource runtimeDiagram;
287         private Resource entry;
288         
289         GroupListener(Session session, Resource runtimeDiagram, Resource entry, StyleBase<T> style, Group group, Observer observer) {
290                 super(style, group, observer);
291                 this.style = style;
292                 this.session = session;
293                 this.runtimeDiagram = runtimeDiagram;
294                 this.entry = entry;
295         }
296         
297           @Override
298           public void add(final Resource item) {
299
300                   if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
301                   System.out.println(style + ": added to group " + group + ": " + item);
302
303               session.asyncRequest(
304                       style.getStyleCalculationRequest(runtimeDiagram, entry, item),
305                       style.getStyleResultListener(this, item, group, observer, runtimeDiagram)
306               );
307
308               super.add(item);
309           }
310           @Override
311           public void remove(Resource item) {
312               if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
313                   System.out.println(style + ": removed from group " + group + ": " + item);
314
315               StyleBaseData.getInstance().removeItem(style, item);
316
317               // TODO: do something here to dispose of ObserverGroupValueListeners?
318               super.remove(item);
319           }  
320           
321     }
322     
323     /* (non-Javadoc)
324      * @see org.simantics.diagram.profile.Style#activate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
325      */
326     @Override
327     public final void activate(RequestProcessor backend, final Resource runtimeDiagram, final Resource entry, final Group group, final EvaluationContext observer) throws DatabaseException {
328
329         ObserverGroupListener listener = getListener(runtimeDiagram, group);
330
331         if (listener == null || listener.isDisposed()) {
332
333             if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
334                 System.out.println("activate(" + runtimeDiagram + ", " + group + ", " + observer);
335
336             listener = new GroupListener<Result>(backend.getSession(), runtimeDiagram, entry, this, group, observer);
337
338             StyleBaseData.getInstance().putListener(new Tuple3(this, runtimeDiagram, group), listener);
339
340             group.trackItems(backend, runtimeDiagram, listener);
341
342         }
343
344         // Register this entry in the listener
345         listener.addEntry(entry);
346     }
347
348     /**
349      * Used to customize the identity given to graph requests made for this
350      * style. Default identity is getClass().
351      * 
352      * @return identity object used in graph requests made by this style
353      */
354     protected Object getIdentity(Resource entry) {
355         return new Pair<Class<?>, Resource>(getClass(), entry);
356     }
357
358     /**
359      * @param configuration
360      * @param runtimeDiagram
361      * @param item
362      * @return
363      */
364     protected Read<Result> getStyleCalculationRequest(Resource runtimeDiagram, final Resource entry, Resource item) {
365         return new TernaryRead<Object, Resource, Resource, Result>(getIdentity(entry), runtimeDiagram, item) {
366             @Override
367             public Result perform(ReadGraph graph) throws DatabaseException {
368                 boolean oldSynchronous = graph.getSynchronous();
369                 try {
370                     graph.setSynchronous(false);
371                     Result result = calculateStyle(graph, parameter2, entry, parameter3);
372                     if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
373                         System.out.println(StyleBase.this + ": calculated style result for " + NameUtils.getSafeName(graph, parameter3, true) + ": " + result);
374                     return result;
375                 } finally {
376                     graph.setSynchronous(oldSynchronous);
377                 }
378             }
379         };
380     }
381
382     /**
383      * @param groupListener
384      * @param item
385      * @param group
386      * @param observer
387      * @param runtimeDiagram 
388      * @return
389      */
390     protected Listener<Result> getStyleResultListener(ObserverGroupListener groupListener, final Resource item,
391             Group group, Observer observer, final Resource runtimeDiagram) {
392         return new ObserverGroupValueListener<Result>(groupListener, observer, group, item) {
393             @Override
394             public void execute(Result result) {
395                 if (DebugPolicy.DEBUG_PROFILE_STYLE_GROUP_TRACKING)
396                     System.out.println(StyleBase.this + ": style result changed for " + item + ": " + result);
397                 styleResultChanged(observer, runtimeDiagram, item, result);
398             }
399         };
400     }
401
402     /* (non-Javadoc)
403      * @see org.simantics.diagram.profile.Style#deactivate(org.simantics.db.RequestProcessor, org.simantics.db.Resource, org.simantics.db.layer0.variable.Variable, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
404      */
405     @Override
406     public final void deactivate(Resource runtimeDiagram, Resource entry, Group group,
407             EvaluationContext observer) {
408
409         ObserverGroupListener listener = getListener(runtimeDiagram, group);
410         if (listener != null) {
411
412             if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
413                 System.out.println("deactivate(" + runtimeDiagram + ", " + group + ", " + observer);
414
415             IDiagram diagram = observer.getConstant(ProfileKeys.DIAGRAM);
416
417             listener.removeEntry(entry);
418             if (!listener.hasEntries()) {
419                 listener.dispose();
420                 StyleBaseData.getInstance().removeListener(new Tuple3(this, runtimeDiagram, group));
421             }
422
423             // This was too eager when multiple groups were tracked!
424             //values.clear();
425             if (diagram != null) {
426                 cleanupItems(observer, diagram, listener.getItems().toArray());
427                 diagram = null;
428             }
429             observer.update();
430         }
431
432     }
433
434     /* (non-Javadoc)
435      * @see org.simantics.diagram.profile.Style#apply(org.simantics.g2d.diagram.IDiagram, org.simantics.diagram.profile.Group, org.simantics.diagram.profile.Observer)
436      */
437     @Override
438     public final void apply(Resource entry, Group group, final EvaluationContext evaluationContext) {
439
440         ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);
441         
442         assert context.getThreadAccess().currentThreadAccess();
443
444         ObserverGroupListener listener = getListener(evaluationContext.getResource(), group);
445         if (listener == null) {
446             System.out.println(this + "(" + getClass().getSimpleName() + ") had no listener for " + evaluationContext.getResource() + " " + group);
447             return;
448         }
449         
450         final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);
451
452         if (DebugPolicy.DEBUG_PROFILE_STYLE_APPLICATION)
453             System.out.println(StyleBase.this + ": applying style for items: " + listener.getItems());
454
455         
456         StyleBaseData data = StyleBaseData.getInstance();
457         
458         data.applyRemovals(evaluationContext, this);
459         
460         for (Object item : listener.getItems()) {
461             Result value = data.getValue(new Tuple3(this, evaluationContext.getResource(), item));
462             applyStyleForItem(evaluationContext, map, item, value);
463         }
464         
465     }
466
467     /**
468      * This is ran when this profile entry gets deactivated after being first
469      * active. It allows cleaning up scene graph left-overs for the listened set
470      * of items before deactivation. It will invoke
471      * {@link #cleanupStyleForElement(IDiagram, IElement)} for each diagram element observed
472      * before deactivation in the AWT thread. If the profile observer is
473      * disposed in between scheduling to AWT thread, the method will do nothing.
474      * 
475      * @param observer the diagram profile observer
476      * @param diagram the diagram this profile system is working with
477      * @param items the diagram data items that need to be cleaned up
478      */
479     protected final void cleanupItems(final EvaluationContext evaluationContext, final IDiagram diagram, final Object[] items) {
480
481         ICanvasContext context = evaluationContext.getConstant(ProfileKeys.CANVAS);
482
483         context.getThreadAccess().asyncExec(new Runnable() {
484                 
485             @Override
486             public void run() {
487                 
488                 if (evaluationContext.isDisposed())
489                     return;
490
491                 final DataNodeMap map = evaluationContext.getConstant(ProfileKeys.NODE_MAP);
492                 
493                 if (DebugPolicy.DEBUG_PROFILE_STYLE_ACTIVATION)
494                     System.out.println(this + ".cleanupItems(" + evaluationContext + ", " + diagram + ", " + Arrays.toString(items));
495
496                 for (Object item : items) {
497                     cleanupStyleForItem(evaluationContext, map, item);
498                 }
499             }
500         });
501     }
502     
503     private ObserverGroupListener getListener(Resource runtime, Group group) {
504         return StyleBaseData.getInstance().getListener(new Tuple3(this, runtime, group));
505     }
506
507 }