]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/layer/GraphLayerManager.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / synchronization / graph / layer / GraphLayerManager.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.synchronization.graph.layer;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.HashSet;\r
17 import java.util.Set;\r
18 import java.util.concurrent.ConcurrentHashMap;\r
19 import java.util.concurrent.ConcurrentMap;\r
20 import java.util.concurrent.atomic.AtomicInteger;\r
21 \r
22 import org.simantics.databoard.Bindings;\r
23 import org.simantics.db.AsyncReadGraph;\r
24 import org.simantics.db.ReadGraph;\r
25 import org.simantics.db.Resource;\r
26 import org.simantics.db.WriteGraph;\r
27 import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;\r
28 import org.simantics.db.exception.CancelTransactionException;\r
29 import org.simantics.db.exception.DatabaseException;\r
30 import org.simantics.db.exception.ServiceException;\r
31 import org.simantics.db.procedure.AsyncProcedure;\r
32 import org.simantics.diagram.stubs.DiagramResource;\r
33 import org.simantics.diagram.synchronization.IModificationQueue;\r
34 import org.simantics.diagram.synchronization.ModificationAdapter;\r
35 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
36 import org.simantics.g2d.diagram.DiagramHints;\r
37 import org.simantics.g2d.diagram.IDiagram;\r
38 import org.simantics.g2d.element.ElementHints;\r
39 import org.simantics.g2d.element.IElement;\r
40 import org.simantics.g2d.element.handler.ElementLayers;\r
41 import org.simantics.g2d.layers.IEditableLayer;\r
42 import org.simantics.g2d.layers.IEditableLayer.ILayerListener;\r
43 import org.simantics.g2d.layers.IEditableLayer.LayerChangeEvent;\r
44 import org.simantics.g2d.layers.ILayer;\r
45 import org.simantics.g2d.layers.ILayers;\r
46 import org.simantics.g2d.layers.ILayersEditor;\r
47 import org.simantics.g2d.layers.ILayersEditor.ILayersEditorListener;\r
48 import org.simantics.g2d.layers.SimpleLayer;\r
49 import org.simantics.g2d.layers.SimpleLayers;\r
50 import org.simantics.layer0.Layer0;\r
51 \r
52 /**\r
53  * @author Tuukka Lehtonen\r
54  */\r
55 public class GraphLayerManager {\r
56 \r
57     class LayerListener implements ILayersEditorListener, ILayerListener {\r
58         @Override\r
59         public void layerAdded(final ILayer layer) {\r
60             modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null);\r
61             modificationQueue.flush();\r
62         }\r
63 \r
64         @Override\r
65         public void layerRemoved(final ILayer layer) {\r
66             modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null);\r
67             modificationQueue.flush();\r
68         }\r
69 \r
70         @Override\r
71         public void layerActivated(ILayer layer) {\r
72             postActivation(layer, true);\r
73         }\r
74 \r
75         @Override\r
76         public void layerDeactivated(ILayer layer) {\r
77             postActivation(layer, false);\r
78         }\r
79 \r
80         @Override\r
81         public void ignoreFocusChanged(boolean value) {\r
82             // Ignore, not written to graph.\r
83         }\r
84 \r
85         @Override\r
86         public void ignoreVisibilityChanged(boolean value) {\r
87             // Ignore, not written to graph.\r
88         }\r
89 \r
90         void postActivation(ILayer layer, boolean activated) {\r
91             modificationQueue.offer(new LayerActivation(layer, activated), null);\r
92             modificationQueue.flush();\r
93         }\r
94 \r
95         @Override\r
96         public void layerChanged(LayerChangeEvent event) {\r
97             LayerChange change = null;\r
98             if (IEditableLayer.PROP_NAME.equals(event.getProperty())) {\r
99                 String oldName = (String) event.getOldValue();\r
100                 String newName = (String) event.getNewValue();\r
101                 synchronized (layers) {\r
102                     GraphLayer gl = layers.remove(oldName);\r
103                     if (gl == null)\r
104                         return;\r
105                     layers.put(newName, gl.withName(newName));\r
106                     change = new LayerChange(event, gl);\r
107                 }\r
108             }\r
109             if (change != null) {\r
110                 modificationQueue.offer(change, null);\r
111                 modificationQueue.flush();\r
112             }\r
113         }\r
114     }\r
115 \r
116     public static final boolean       DEBUG_LAYERS  = false;\r
117 \r
118     IModificationQueue                modificationQueue;\r
119     Resource                          diagram;\r
120     Layer0                            l0;\r
121     DiagramResource                   dia;\r
122 \r
123     /**\r
124      * All the layers currently loaded from the diagram.\r
125      */\r
126     ConcurrentMap<String, GraphLayer> layers        = new ConcurrentHashMap<String, GraphLayer>();\r
127 \r
128     SimpleLayers                      layerEditor;\r
129 \r
130     /**\r
131      * The listener for ILayersEditor and all IEditableLayer.\r
132      */\r
133     LayerListener                     layerListener = new LayerListener();\r
134 \r
135     public GraphLayerManager(ReadGraph graph, IModificationQueue modificationQueue, Resource diagram) {\r
136         this.modificationQueue = modificationQueue;\r
137         this.diagram = diagram;\r
138         this.l0 = Layer0.getInstance(graph);\r
139         this.dia = DiagramResource.getInstance(graph);\r
140     }\r
141 \r
142     public void dispose() {\r
143         for (ILayer layer : layerEditor.getLayers()) {\r
144             if (layer instanceof IEditableLayer) {\r
145                 ((IEditableLayer) layer).removeLayerListener(layerListener);\r
146             }\r
147         }\r
148         layers.clear();\r
149     }\r
150 \r
151     Resource getDiagramResource() {\r
152         return diagram;\r
153     }\r
154 \r
155     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
156     // LAYERS BEGIN\r
157     // ------------------------------------------------------------------------\r
158 \r
159     class CreateLayer extends ModificationAdapter {\r
160         final ILayer   layer;\r
161 \r
162         final Resource diagram;\r
163 \r
164         public CreateLayer(ILayer layer, Resource diagram) {\r
165             super(LOW_PRIORITY);\r
166             assert layer != null;\r
167             this.layer = layer;\r
168             this.diagram = diagram;\r
169 \r
170             if (layer instanceof IEditableLayer) {\r
171                 ((IEditableLayer) layer).addLayerListener(layerListener);\r
172             }\r
173         }\r
174 \r
175         @Override\r
176         public void perform(WriteGraph g) throws Exception {\r
177             String newName = layer.getName();\r
178             for (Resource l : g.getObjects(diagram, dia.HasLayer)) {\r
179                 String name = g.getRelatedValue(l, l0.HasName);\r
180                 if (newName.equals(name)) {\r
181                     return;\r
182                 }\r
183             }\r
184             GraphLayer l = new GraphLayerUtil(g).createLayer(newName, false);\r
185             g.claim(diagram, dia.HasLayer, l.getLayer());\r
186             layers.put(newName, l);\r
187         }\r
188     }\r
189 \r
190     class RemoveLayer extends ModificationAdapter {\r
191         final ILayer   layer;\r
192 \r
193         final Resource diagram;\r
194 \r
195         public RemoveLayer(ILayer layer, Resource diagram) {\r
196             super(LOW_PRIORITY);\r
197             assert layer != null;\r
198             this.layer = layer;\r
199             this.diagram = diagram;\r
200 \r
201             if (layer instanceof IEditableLayer) {\r
202                 ((IEditableLayer) layer).removeLayerListener(layerListener);\r
203             }\r
204         }\r
205 \r
206         @Override\r
207         public void perform(WriteGraph g) throws Exception {\r
208             String removedName = layer.getName();\r
209             for (Resource l : g.getObjects(diagram, dia.HasLayer)) {\r
210                 String name = g.getRelatedValue(l, l0.HasName);\r
211                 if (removedName.equals(name)) {\r
212                     g.denyStatement(diagram, dia.HasLayer, l);\r
213                     deleteLayer(g, l);\r
214 \r
215                     // NOTE: leave the layer tags intact, remove them gradually\r
216                     // by checking the validity of all layer tags during element\r
217                     // writeback.\r
218                     layers.remove(name);\r
219                     return;\r
220                 }\r
221             }\r
222         }\r
223 \r
224         void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException {\r
225             g.deny(layer);\r
226         }\r
227     }\r
228 \r
229     class LayerActivation extends ModificationAdapter {\r
230         final ILayer  layer;\r
231 \r
232         final boolean activated;\r
233 \r
234         public LayerActivation(ILayer layer, boolean activated) {\r
235             super(LOW_PRIORITY);\r
236             assert layer != null;\r
237             this.layer = layer;\r
238             this.activated = activated;\r
239         }\r
240 \r
241         @Override\r
242         public void perform(WriteGraph g) throws Exception {\r
243             GraphLayer gl = layers.get(layer.getName());\r
244             if (gl == null)\r
245                 throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName());\r
246 \r
247             g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated));\r
248         }\r
249     }\r
250 \r
251     class LayerChange extends ModificationAdapter {\r
252         final LayerChangeEvent event;\r
253 \r
254         final GraphLayer       gl;\r
255 \r
256         public LayerChange(LayerChangeEvent event, GraphLayer gl) {\r
257             super(LOW_PRIORITY);\r
258             assert event != null;\r
259             assert gl != null;\r
260             this.event = event;\r
261             this.gl = gl;\r
262         }\r
263 \r
264         @Override\r
265         public void perform(WriteGraph g) throws Exception {\r
266 //            Resource name = g.getSingleObject(gl.getLayer(), b.HasName);\r
267 //            g.claimValue(name, event.getSource().getName());\r
268             g.claimLiteral(gl.getLayer(), l0.HasName, event.getSource().getName(), Bindings.STRING);\r
269         }\r
270     }\r
271 \r
272     void deleteTag(WriteGraph g, Resource tag) throws DatabaseException {\r
273         g.deny(tag);\r
274     }\r
275 \r
276     Collection<GraphLayer> getActiveLayers(ReadGraph g) throws DatabaseException {\r
277         Collection<GraphLayer> result = new ArrayList<GraphLayer>();\r
278         for (GraphLayer gl : layers.values()) {\r
279             Boolean active = g.getPossibleRelatedValue(gl.getLayer(), dia.IsActive);\r
280             if (Boolean.TRUE.equals(active)) {\r
281                 result.add(gl);\r
282             }\r
283         }\r
284         return result;\r
285     }\r
286 \r
287     void tagElementWithActiveLayers(WriteGraph g, Resource element) throws DatabaseException {\r
288         Collection<GraphLayer> activeLayers = getActiveLayers(g);\r
289         for (GraphLayer activeLayer : activeLayers) {\r
290             DiagramGraphUtil.tag(g, element, activeLayer.getVisible(), true);\r
291             DiagramGraphUtil.tag(g, element, activeLayer.getFocusable(), true);\r
292         }\r
293     }\r
294 \r
295     GraphLayer loadLayer(ReadGraph g, Resource layer) throws DatabaseException {\r
296         String name = g.getRelatedValue(layer, l0.HasName);\r
297         Resource visible = g.getSingleObject(layer, dia.HasVisibleTag);\r
298         Resource focusable = g.getSingleObject(layer, dia.HasFocusableTag);\r
299         return new GraphLayer(name, layer, visible, focusable);\r
300     }\r
301 \r
302     public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException {\r
303         SimpleLayers result = new SimpleLayers();\r
304         ConcurrentMap<String, GraphLayer> newLayers = new ConcurrentHashMap<String, GraphLayer>();\r
305 \r
306         String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS);\r
307         if (fixed != null) {\r
308 \r
309 //            for (String name : fixed) {\r
310 //                SimpleLayer l = new SimpleLayer(name);\r
311 //                result.addLayer(l);\r
312 //                result.activate(l);\r
313 //            }\r
314 \r
315             // We need to put GraphLayer to newLayers so...\r
316             for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {\r
317                 GraphLayer gl = loadLayer(g, layer);\r
318                 for (String name : fixed) {\r
319                     if (name.equals(gl.getName())) {\r
320                         SimpleLayer l = new SimpleLayer(gl.getName());\r
321                         newLayers.put(gl.getName(), gl);\r
322                         result.addLayer(l);\r
323                         result.activate(l);\r
324                     }\r
325                 }\r
326             }\r
327 \r
328         } else {\r
329 \r
330             if (DEBUG_LAYERS)\r
331                 System.out.println("Loading layers");\r
332 \r
333             for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {\r
334                 GraphLayer gl = loadLayer(g, layer);\r
335                 SimpleLayer l = new SimpleLayer(gl.getName());\r
336 \r
337                 newLayers.put(gl.getName(), gl);\r
338                 result.addLayer(l);\r
339 \r
340                 Boolean active = g.getPossibleRelatedValue(layer, dia.IsActive);\r
341                 if (active == null)\r
342                     active = Boolean.FALSE;\r
343 \r
344                 if (DEBUG_LAYERS)\r
345                     System.out.println("    Loaded " + (active ? "active" : "inactive") + " layer '" + gl.getName() + "'");\r
346 \r
347                 l.addLayerListener(layerListener);\r
348                 if (active)\r
349                     result.activate(l);\r
350             }\r
351 \r
352             if (DEBUG_LAYERS)\r
353                 System.out.println("Loaded " + newLayers.size() + " layers");\r
354 \r
355         }\r
356 \r
357         this.layers = newLayers;\r
358         this.layerEditor = result;\r
359         this.layerEditor.addListener(layerListener);\r
360 \r
361         return result;\r
362     }\r
363 \r
364     public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element)\r
365     throws DatabaseException {\r
366         if (DEBUG_LAYERS)\r
367             System.out.println("Loading layers for element " + element + " - " + e);\r
368 \r
369         Set<ILayer> visible = null;\r
370         Set<ILayer> focusable = null;\r
371 \r
372         for (ILayer l : layersEditor.getLayers()) {\r
373             GraphLayer gl = layers.get(l.getName());\r
374             if (gl != null) {\r
375                 if (graph.hasStatement(element, gl.getVisible(), element)) {\r
376                     if (visible == null)\r
377                         visible = new HashSet<ILayer>(4);\r
378                     visible.add(l);\r
379                     if (DEBUG_LAYERS)\r
380                         System.out.println("    Visible on layer '" + gl.getName() + "'");\r
381                 }\r
382                 if (graph.hasStatement(element, gl.getFocusable(), element)) {\r
383                     if (focusable == null)\r
384                         focusable = new HashSet<ILayer>(4);\r
385                     focusable.add(l);\r
386                     if (DEBUG_LAYERS)\r
387                         System.out.println("    Focusable on layer '" + gl.getName() + "'");\r
388                 }\r
389             }\r
390         }\r
391 \r
392         if (visible == null)\r
393             visible = new HashSet<ILayer>(1);\r
394         if (focusable == null)\r
395             focusable = new HashSet<ILayer>(1);\r
396 \r
397         e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
398         e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
399     }\r
400 \r
401     /**\r
402      * @param graph\r
403      * @param layersEditor\r
404      * @param e\r
405      * @param element\r
406      * @param procedure a procedure whose exception method may be called 0 to\r
407      *        many times depending on how many errors occur during layer loading\r
408      * @throws DatabaseException\r
409      */\r
410     public void loadLayersForElement(AsyncReadGraph graph, ILayersEditor layersEditor, final IElement e,\r
411             Resource element, final AsyncProcedure<IElement> callback) {\r
412         if (DEBUG_LAYERS)\r
413             System.out.println("Loading layers for element " + element + " - " + e);\r
414 \r
415         final Set<ILayer> visible = new HashSet<ILayer>(2);\r
416         final Set<ILayer> focusable = new HashSet<ILayer>(2);\r
417 \r
418         // NOTE: must not set layer hints into element until the layer sets have\r
419         // been properly loaded.\r
420 \r
421         Set<ILayer> allLayers = layersEditor.getLayers();\r
422         if (allLayers.isEmpty()) {\r
423             e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
424             e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
425             callback.execute(graph, e);\r
426             return;\r
427         }\r
428 \r
429         final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2);\r
430 \r
431         for (final ILayer l : allLayers) {\r
432             final GraphLayer gl = layers.get(l.getName());\r
433             if (gl != null) {\r
434                 graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter<Boolean>() {\r
435                     @Override\r
436                     public void execute(AsyncReadGraph graph, Boolean result) {\r
437                         synchronized (visible) {\r
438                             visible.add(l);\r
439                         }\r
440                         if (DEBUG_LAYERS)\r
441                             System.out.println("    Visible on layer '" + gl.getName() + "'");\r
442                         if (ready.decrementAndGet() == 0) {\r
443                             e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
444                             e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
445                             callback.execute(graph, e);\r
446                         }\r
447                     }\r
448                     @Override\r
449                     public void exception(AsyncReadGraph graph, Throwable t) {\r
450                         callback.exception(graph, t);\r
451                     }\r
452                 });\r
453                 graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter<Boolean>() {\r
454                     @Override\r
455                     public void execute(AsyncReadGraph graph, Boolean result) {\r
456                         synchronized (focusable) {\r
457                             focusable.add(l);\r
458                         }\r
459                         if (DEBUG_LAYERS)\r
460                             System.out.println("    Focusable on layer '" + gl.getName() + "'");\r
461                         if (ready.decrementAndGet() == 0) {\r
462                             e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
463                             e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
464                             callback.execute(graph, e);\r
465                         }\r
466                     }\r
467                     @Override\r
468                     public void exception(AsyncReadGraph graph, Throwable t) {\r
469                         callback.exception(graph, t);\r
470                     }\r
471                 });\r
472 \r
473             } else {\r
474 \r
475                 if (ready.addAndGet(-2) == 0) {\r
476                     e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
477                     e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
478                     callback.execute(graph, e);\r
479                 }\r
480 \r
481             }\r
482         }\r
483     }\r
484 \r
485     void putElementOnVisibleLayers(IDiagram diagram, IElement element) {\r
486         // Make the new element visible and focusable on all currently\r
487         // active layers.\r
488         ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);\r
489         if (diagramLayers != null) {\r
490             element.setHint(ElementHints.KEY_VISIBLE_LAYERS, new HashSet<ILayer>());\r
491             element.setHint(ElementHints.KEY_FOCUS_LAYERS, new HashSet<ILayer>());\r
492             Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();\r
493 \r
494             if (DEBUG_LAYERS)\r
495                 System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers);\r
496 \r
497             for (ElementLayers elementLayers : element.getElementClass().getItemsByClass(ElementLayers.class)) {\r
498                 for (ILayer layer : visibleLayers) {\r
499                     elementLayers.setVisibility(element, layer, true);\r
500                     elementLayers.setFocusability(element, layer, true);\r
501                 }\r
502             }\r
503         }\r
504     }\r
505 \r
506     public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException {\r
507         // Make the new element visible and focusable on all currently\r
508         // active layers.\r
509         ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);\r
510         if (diagramLayers != null) {\r
511             Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();\r
512 \r
513             if (DEBUG_LAYERS)\r
514                 System.out.println("Marking element visible and focusable in the graph on visible layers: "\r
515                         + visibleLayers);\r
516 \r
517             for (ILayer layer : visibleLayers) {\r
518                 GraphLayer gl = layers.get(layer.getName());\r
519                 if (gl != null) {\r
520                     DiagramGraphUtil.tag(g, element, gl.getVisible(), true);\r
521                     DiagramGraphUtil.tag(g, element, gl.getFocusable(), true);\r
522                 }\r
523             }\r
524         }\r
525     }\r
526 \r
527     public void removeFromAllLayers(WriteGraph graph, Resource element) throws ServiceException {\r
528         // Remove element from all layers.\r
529         graph.deny(element, dia.IsVisible);\r
530         graph.deny(element, dia.IsFocusable);\r
531     }\r
532 \r
533     public GraphLayer getGraphLayer(String name) {\r
534         return layers.get(name);\r
535     }\r
536 \r
537 }\r