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