1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.synchronization.graph.layer;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashSet;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ConcurrentMap;
20 import java.util.concurrent.atomic.AtomicInteger;
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;
54 * @author Tuukka Lehtonen
56 public class GraphLayerManager {
58 class LayerListener implements ILayersEditorListener, ILayerListener {
60 public void layerAdded(final ILayer layer) {
61 modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null);
62 modificationQueue.flush();
66 public void layerRemoved(final ILayer layer) {
67 modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null);
68 modificationQueue.flush();
72 public void layerActivated(ILayer layer) {
73 postActivation(layer, true);
77 public void layerDeactivated(ILayer layer) {
78 postActivation(layer, false);
82 public void ignoreFocusChanged(boolean value) {
83 // Ignore, not written to graph.
87 public void ignoreVisibilityChanged(boolean value) {
88 // Ignore, not written to graph.
91 void postActivation(ILayer layer, boolean activated) {
92 modificationQueue.offer(new LayerActivation(layer, activated), null);
93 modificationQueue.flush();
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);
106 layers.put(newName, gl.withName(newName));
107 change = new LayerChange(event, gl);
110 if (change != null) {
111 modificationQueue.offer(change, null);
112 modificationQueue.flush();
117 public static final boolean DEBUG_LAYERS = false;
119 IModificationQueue modificationQueue;
125 * All the layers currently loaded from the diagram.
127 ConcurrentMap<String, GraphLayer> layers = new ConcurrentHashMap<String, GraphLayer>();
129 SimpleLayers layerEditor;
132 * The listener for ILayersEditor and all IEditableLayer.
134 LayerListener layerListener = new LayerListener();
136 boolean disposed = false;
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);
145 public void dispose() {
146 for (ILayer layer : layerEditor.getLayers()) {
147 if (layer instanceof IEditableLayer) {
148 ((IEditableLayer) layer).removeLayerListener(layerListener);
155 Resource getDiagramResource() {
159 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
161 // ------------------------------------------------------------------------
163 class CreateLayer extends ModificationAdapter {
166 final Resource diagram;
168 public CreateLayer(ILayer layer, Resource diagram) {
170 assert layer != null;
172 this.diagram = diagram;
174 if (layer instanceof IEditableLayer) {
175 ((IEditableLayer) layer).addLayerListener(layerListener);
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)) {
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);
196 class RemoveLayer extends ModificationAdapter {
199 final Resource diagram;
201 public RemoveLayer(ILayer layer, Resource diagram) {
203 assert layer != null;
205 this.diagram = diagram;
207 if (layer instanceof IEditableLayer) {
208 ((IEditableLayer) layer).removeLayerListener(layerListener);
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);
221 // NOTE: leave the layer tags intact, remove them gradually
222 // by checking the validity of all layer tags during element
230 void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException {
235 class LayerActivation extends ModificationAdapter {
238 final boolean activated;
240 public LayerActivation(ILayer layer, boolean activated) {
242 assert layer != null;
244 this.activated = activated;
248 public void perform(WriteGraph g) throws Exception {
249 GraphLayer gl = layers.get(layer.getName());
251 throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName());
253 g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated));
257 class LayerChange extends ModificationAdapter {
258 final LayerChangeEvent event;
262 public LayerChange(LayerChangeEvent event, GraphLayer gl) {
264 assert event != null;
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);
278 void deleteTag(WriteGraph g, Resource tag) throws DatabaseException {
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)) {
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);
301 public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException {
303 layerEditor = new SimpleLayers();
304 layerEditor.addLayerEditorListener(layerListener);
306 String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS);
308 g.syncRequest(new ResourceRead<LayersSpec>(diagramResource) {
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);
318 return new LayersSpec(gls);
321 }, new Listener<LayersSpec>() {
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<>();
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);
336 visibleLayers.add(l);
344 System.out.println("Loading layers");
346 for (GraphLayer gl : layersSpec.getGraphLayers()) {
347 ILayer l = gl.getILayer();
349 newLayers.put(gl.getName(), gl);
352 System.out.println(" Loaded " + (gl.isActive() ? "active" : "inactive") + " layer '" + gl.getName() + "'");
354 if (l instanceof IEditableLayer)
355 ((IEditableLayer) l).addLayerListener(layerListener);
359 visibleLayers.add(l);
363 // Show all and focus all by default if there are no layers
366 if (newLayers.isEmpty()) {
367 layerEditor.setIgnoreVisibilitySettings(true);
368 layerEditor.setIgnoreFocusSettings(true);
372 System.out.println("Loaded " + newLayers.size() + " layers");
374 layerEditor.update(allLayers, visibleLayers);
380 public void exception(Throwable t) {
385 public boolean isDisposed() {
395 public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element)
396 throws DatabaseException {
398 System.out.println("Loading layers for element " + element + " - " + e);
400 Set<ILayer> visible = null;
401 Set<ILayer> focusable = null;
403 for (ILayer l : layersEditor.getLayers()) {
404 GraphLayer gl = layers.get(l.getName());
406 if (graph.hasStatement(element, gl.getVisible(), element)) {
408 visible = new HashSet<ILayer>(4);
411 System.out.println(" Visible on layer '" + gl.getName() + "'");
413 if (graph.hasStatement(element, gl.getFocusable(), element)) {
414 if (focusable == null)
415 focusable = new HashSet<ILayer>(4);
418 System.out.println(" Focusable on layer '" + gl.getName() + "'");
424 visible = new HashSet<ILayer>(1);
425 if (focusable == null)
426 focusable = new HashSet<ILayer>(1);
428 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
429 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
434 * @param layersEditor
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
441 public void loadLayersForElement(AsyncReadGraph graph, ILayers layers2, final IElement e,
442 Resource element, final AsyncProcedure<IElement> callback) {
444 System.out.println("Loading layers for element " + element + " - " + e);
446 final Set<ILayer> visible = new HashSet<ILayer>(2);
447 final Set<ILayer> focusable = new HashSet<ILayer>(2);
449 // NOTE: must not set layer hints into element until the layer sets have
450 // been properly loaded.
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);
460 final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2);
462 for (final ILayer l : allLayers) {
463 final GraphLayer gl = layers.get(l.getName());
465 graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter<Boolean>() {
467 public void execute(AsyncReadGraph graph, Boolean result) {
469 synchronized (visible) {
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);
482 public void exception(AsyncReadGraph graph, Throwable t) {
483 callback.exception(graph, t);
486 graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter<Boolean>() {
488 public void execute(AsyncReadGraph graph, Boolean result) {
490 synchronized (focusable) {
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);
503 public void exception(AsyncReadGraph graph, Throwable t) {
504 callback.exception(graph, t);
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);
520 void putElementOnVisibleLayers(IDiagram diagram, IElement element) {
521 // Make the new element visible and focusable on all currently
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();
530 System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers);
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);
541 public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException {
542 // Make the new element visible and focusable on all currently
544 ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);
545 if (diagramLayers != null) {
546 Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();
549 System.out.println("Marking element visible and focusable in the graph on visible layers: "
552 for (ILayer layer : visibleLayers) {
553 GraphLayer gl = layers.get(layer.getName());
555 gl.forEachTag(tag -> DiagramGraphUtil.tag(g, element, tag, true));
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);
567 public GraphLayer getGraphLayer(String name) {
568 return layers.get(name);