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.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;
52 * @author Tuukka Lehtonen
54 public class GraphLayerManager {
56 class LayerListener implements ILayersEditorListener, ILayerListener {
58 public void layerAdded(final ILayer layer) {
59 modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null);
60 modificationQueue.flush();
64 public void layerRemoved(final ILayer layer) {
65 modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null);
66 modificationQueue.flush();
70 public void layerActivated(ILayer layer) {
71 postActivation(layer, true);
75 public void layerDeactivated(ILayer layer) {
76 postActivation(layer, false);
80 public void ignoreFocusChanged(boolean value) {
81 // Ignore, not written to graph.
85 public void ignoreVisibilityChanged(boolean value) {
86 // Ignore, not written to graph.
89 void postActivation(ILayer layer, boolean activated) {
90 modificationQueue.offer(new LayerActivation(layer, activated), null);
91 modificationQueue.flush();
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);
104 layers.put(newName, gl.withName(newName));
105 change = new LayerChange(event, gl);
108 if (change != null) {
109 modificationQueue.offer(change, null);
110 modificationQueue.flush();
115 public static final boolean DEBUG_LAYERS = false;
117 IModificationQueue modificationQueue;
123 * All the layers currently loaded from the diagram.
125 ConcurrentMap<String, GraphLayer> layers = new ConcurrentHashMap<String, GraphLayer>();
127 SimpleLayers layerEditor;
130 * The listener for ILayersEditor and all IEditableLayer.
132 LayerListener layerListener = new LayerListener();
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);
141 public void dispose() {
142 for (ILayer layer : layerEditor.getLayers()) {
143 if (layer instanceof IEditableLayer) {
144 ((IEditableLayer) layer).removeLayerListener(layerListener);
150 Resource getDiagramResource() {
154 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
156 // ------------------------------------------------------------------------
158 class CreateLayer extends ModificationAdapter {
161 final Resource diagram;
163 public CreateLayer(ILayer layer, Resource diagram) {
165 assert layer != null;
167 this.diagram = diagram;
169 if (layer instanceof IEditableLayer) {
170 ((IEditableLayer) layer).addLayerListener(layerListener);
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)) {
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);
191 class RemoveLayer extends ModificationAdapter {
194 final Resource diagram;
196 public RemoveLayer(ILayer layer, Resource diagram) {
198 assert layer != null;
200 this.diagram = diagram;
202 if (layer instanceof IEditableLayer) {
203 ((IEditableLayer) layer).removeLayerListener(layerListener);
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);
216 // NOTE: leave the layer tags intact, remove them gradually
217 // by checking the validity of all layer tags during element
225 void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException {
230 class LayerActivation extends ModificationAdapter {
233 final boolean activated;
235 public LayerActivation(ILayer layer, boolean activated) {
237 assert layer != null;
239 this.activated = activated;
243 public void perform(WriteGraph g) throws Exception {
244 GraphLayer gl = layers.get(layer.getName());
246 throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName());
248 g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated));
252 class LayerChange extends ModificationAdapter {
253 final LayerChangeEvent event;
257 public LayerChange(LayerChangeEvent event, GraphLayer gl) {
259 assert event != null;
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);
273 void deleteTag(WriteGraph g, Resource tag) throws DatabaseException {
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)) {
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);
296 public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException {
298 SimpleLayers result = new SimpleLayers();
299 ConcurrentMap<String, GraphLayer> newLayers = new ConcurrentHashMap<String, GraphLayer>();
301 String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS);
304 // for (String name : fixed) {
305 // SimpleLayer l = new SimpleLayer(name);
306 // result.addLayer(l);
307 // result.activate(l);
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);
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);
328 System.out.println("Loading layers");
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();
335 newLayers.put(gl.getName(), gl);
338 Boolean active = g.getPossibleRelatedValue(layer, dia.IsActive);
340 active = Boolean.FALSE;
343 System.out.println(" Loaded " + (active ? "active" : "inactive") + " layer '" + gl.getName() + "'");
345 if (l instanceof IEditableLayer)
346 ((IEditableLayer) l).addLayerListener(layerListener);
352 System.out.println("Loaded " + newLayers.size() + " layers");
356 this.layers = newLayers;
357 this.layerEditor = result;
358 this.layerEditor.addListener(layerListener);
363 public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element)
364 throws DatabaseException {
366 System.out.println("Loading layers for element " + element + " - " + e);
368 Set<ILayer> visible = null;
369 Set<ILayer> focusable = null;
371 for (ILayer l : layersEditor.getLayers()) {
372 GraphLayer gl = layers.get(l.getName());
374 if (graph.hasStatement(element, gl.getVisible(), element)) {
376 visible = new HashSet<ILayer>(4);
379 System.out.println(" Visible on layer '" + gl.getName() + "'");
381 if (graph.hasStatement(element, gl.getFocusable(), element)) {
382 if (focusable == null)
383 focusable = new HashSet<ILayer>(4);
386 System.out.println(" Focusable on layer '" + gl.getName() + "'");
392 visible = new HashSet<ILayer>(1);
393 if (focusable == null)
394 focusable = new HashSet<ILayer>(1);
396 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
397 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
402 * @param layersEditor
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
409 public void loadLayersForElement(AsyncReadGraph graph, ILayersEditor layersEditor, final IElement e,
410 Resource element, final AsyncProcedure<IElement> callback) {
412 System.out.println("Loading layers for element " + element + " - " + e);
414 final Set<ILayer> visible = new HashSet<ILayer>(2);
415 final Set<ILayer> focusable = new HashSet<ILayer>(2);
417 // NOTE: must not set layer hints into element until the layer sets have
418 // been properly loaded.
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);
428 final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2);
430 for (final ILayer l : allLayers) {
431 final GraphLayer gl = layers.get(l.getName());
433 graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter<Boolean>() {
435 public void execute(AsyncReadGraph graph, Boolean result) {
437 synchronized (visible) {
442 System.out.println(" Visible on layer '" + gl.getName() + "'");
443 if (ready.decrementAndGet() == 0) {
444 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
445 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
446 callback.execute(graph, e);
450 public void exception(AsyncReadGraph graph, Throwable t) {
451 callback.exception(graph, t);
454 graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter<Boolean>() {
456 public void execute(AsyncReadGraph graph, Boolean result) {
458 synchronized (focusable) {
463 System.out.println(" Focusable on layer '" + gl.getName() + "'");
464 if (ready.decrementAndGet() == 0) {
465 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
466 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
467 callback.execute(graph, e);
471 public void exception(AsyncReadGraph graph, Throwable t) {
472 callback.exception(graph, t);
478 if (ready.addAndGet(-2) == 0) {
479 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
480 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
481 callback.execute(graph, e);
488 void putElementOnVisibleLayers(IDiagram diagram, IElement element) {
489 // Make the new element visible and focusable on all currently
491 ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);
492 if (diagramLayers != null) {
493 element.setHint(ElementHints.KEY_VISIBLE_LAYERS, new HashSet<ILayer>());
494 element.setHint(ElementHints.KEY_FOCUS_LAYERS, new HashSet<ILayer>());
495 Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();
498 System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers);
500 for (ElementLayers elementLayers : element.getElementClass().getItemsByClass(ElementLayers.class)) {
501 for (ILayer layer : visibleLayers) {
502 elementLayers.setVisibility(element, layer, true);
503 elementLayers.setFocusability(element, layer, true);
509 public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException {
510 // Make the new element visible and focusable on all currently
512 ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);
513 if (diagramLayers != null) {
514 Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();
517 System.out.println("Marking element visible and focusable in the graph on visible layers: "
520 for (ILayer layer : visibleLayers) {
521 GraphLayer gl = layers.get(layer.getName());
523 gl.forEachTag(tag -> DiagramGraphUtil.tag(g, element, tag, true));
529 public void removeFromAllLayers(WriteGraph graph, Resource element) throws ServiceException {
530 // Remove element from all layers.
531 graph.deny(element, dia.IsVisible);
532 graph.deny(element, dia.IsFocusable);
535 public GraphLayer getGraphLayer(String name) {
536 return layers.get(name);