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