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