--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.synchronization.graph.layer;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.ConcurrentMap;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.AsyncReadGraph;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;\r
+import org.simantics.db.exception.CancelTransactionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ServiceException;\r
+import org.simantics.db.procedure.AsyncProcedure;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.IModificationQueue;\r
+import org.simantics.diagram.synchronization.ModificationAdapter;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.ElementLayers;\r
+import org.simantics.g2d.layers.IEditableLayer;\r
+import org.simantics.g2d.layers.IEditableLayer.ILayerListener;\r
+import org.simantics.g2d.layers.IEditableLayer.LayerChangeEvent;\r
+import org.simantics.g2d.layers.ILayer;\r
+import org.simantics.g2d.layers.ILayers;\r
+import org.simantics.g2d.layers.ILayersEditor;\r
+import org.simantics.g2d.layers.ILayersEditor.ILayersEditorListener;\r
+import org.simantics.g2d.layers.SimpleLayer;\r
+import org.simantics.g2d.layers.SimpleLayers;\r
+import org.simantics.layer0.Layer0;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class GraphLayerManager {\r
+\r
+ class LayerListener implements ILayersEditorListener, ILayerListener {\r
+ @Override\r
+ public void layerAdded(final ILayer layer) {\r
+ modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null);\r
+ modificationQueue.flush();\r
+ }\r
+\r
+ @Override\r
+ public void layerRemoved(final ILayer layer) {\r
+ modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null);\r
+ modificationQueue.flush();\r
+ }\r
+\r
+ @Override\r
+ public void layerActivated(ILayer layer) {\r
+ postActivation(layer, true);\r
+ }\r
+\r
+ @Override\r
+ public void layerDeactivated(ILayer layer) {\r
+ postActivation(layer, false);\r
+ }\r
+\r
+ @Override\r
+ public void ignoreFocusChanged(boolean value) {\r
+ // Ignore, not written to graph.\r
+ }\r
+\r
+ @Override\r
+ public void ignoreVisibilityChanged(boolean value) {\r
+ // Ignore, not written to graph.\r
+ }\r
+\r
+ void postActivation(ILayer layer, boolean activated) {\r
+ modificationQueue.offer(new LayerActivation(layer, activated), null);\r
+ modificationQueue.flush();\r
+ }\r
+\r
+ @Override\r
+ public void layerChanged(LayerChangeEvent event) {\r
+ LayerChange change = null;\r
+ if (IEditableLayer.PROP_NAME.equals(event.getProperty())) {\r
+ String oldName = (String) event.getOldValue();\r
+ String newName = (String) event.getNewValue();\r
+ synchronized (layers) {\r
+ GraphLayer gl = layers.remove(oldName);\r
+ if (gl == null)\r
+ return;\r
+ layers.put(newName, gl.withName(newName));\r
+ change = new LayerChange(event, gl);\r
+ }\r
+ }\r
+ if (change != null) {\r
+ modificationQueue.offer(change, null);\r
+ modificationQueue.flush();\r
+ }\r
+ }\r
+ }\r
+\r
+ public static final boolean DEBUG_LAYERS = false;\r
+\r
+ IModificationQueue modificationQueue;\r
+ Resource diagram;\r
+ Layer0 l0;\r
+ DiagramResource dia;\r
+\r
+ /**\r
+ * All the layers currently loaded from the diagram.\r
+ */\r
+ ConcurrentMap<String, GraphLayer> layers = new ConcurrentHashMap<String, GraphLayer>();\r
+\r
+ SimpleLayers layerEditor;\r
+\r
+ /**\r
+ * The listener for ILayersEditor and all IEditableLayer.\r
+ */\r
+ LayerListener layerListener = new LayerListener();\r
+\r
+ public GraphLayerManager(ReadGraph graph, IModificationQueue modificationQueue, Resource diagram) {\r
+ this.modificationQueue = modificationQueue;\r
+ this.diagram = diagram;\r
+ this.l0 = Layer0.getInstance(graph);\r
+ this.dia = DiagramResource.getInstance(graph);\r
+ }\r
+\r
+ public void dispose() {\r
+ for (ILayer layer : layerEditor.getLayers()) {\r
+ if (layer instanceof IEditableLayer) {\r
+ ((IEditableLayer) layer).removeLayerListener(layerListener);\r
+ }\r
+ }\r
+ layers.clear();\r
+ }\r
+\r
+ Resource getDiagramResource() {\r
+ return diagram;\r
+ }\r
+\r
+ // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r
+ // LAYERS BEGIN\r
+ // ------------------------------------------------------------------------\r
+\r
+ class CreateLayer extends ModificationAdapter {\r
+ final ILayer layer;\r
+\r
+ final Resource diagram;\r
+\r
+ public CreateLayer(ILayer layer, Resource diagram) {\r
+ super(LOW_PRIORITY);\r
+ assert layer != null;\r
+ this.layer = layer;\r
+ this.diagram = diagram;\r
+\r
+ if (layer instanceof IEditableLayer) {\r
+ ((IEditableLayer) layer).addLayerListener(layerListener);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void perform(WriteGraph g) throws Exception {\r
+ String newName = layer.getName();\r
+ for (Resource l : g.getObjects(diagram, dia.HasLayer)) {\r
+ String name = g.getRelatedValue(l, l0.HasName);\r
+ if (newName.equals(name)) {\r
+ return;\r
+ }\r
+ }\r
+ GraphLayer l = new GraphLayerUtil(g).createLayer(newName, false);\r
+ g.claim(diagram, dia.HasLayer, l.getLayer());\r
+ layers.put(newName, l);\r
+ }\r
+ }\r
+\r
+ class RemoveLayer extends ModificationAdapter {\r
+ final ILayer layer;\r
+\r
+ final Resource diagram;\r
+\r
+ public RemoveLayer(ILayer layer, Resource diagram) {\r
+ super(LOW_PRIORITY);\r
+ assert layer != null;\r
+ this.layer = layer;\r
+ this.diagram = diagram;\r
+\r
+ if (layer instanceof IEditableLayer) {\r
+ ((IEditableLayer) layer).removeLayerListener(layerListener);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void perform(WriteGraph g) throws Exception {\r
+ String removedName = layer.getName();\r
+ for (Resource l : g.getObjects(diagram, dia.HasLayer)) {\r
+ String name = g.getRelatedValue(l, l0.HasName);\r
+ if (removedName.equals(name)) {\r
+ g.denyStatement(diagram, dia.HasLayer, l);\r
+ deleteLayer(g, l);\r
+\r
+ // NOTE: leave the layer tags intact, remove them gradually\r
+ // by checking the validity of all layer tags during element\r
+ // writeback.\r
+ layers.remove(name);\r
+ return;\r
+ }\r
+ }\r
+ }\r
+\r
+ void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException {\r
+ g.deny(layer);\r
+ }\r
+ }\r
+\r
+ class LayerActivation extends ModificationAdapter {\r
+ final ILayer layer;\r
+\r
+ final boolean activated;\r
+\r
+ public LayerActivation(ILayer layer, boolean activated) {\r
+ super(LOW_PRIORITY);\r
+ assert layer != null;\r
+ this.layer = layer;\r
+ this.activated = activated;\r
+ }\r
+\r
+ @Override\r
+ public void perform(WriteGraph g) throws Exception {\r
+ GraphLayer gl = layers.get(layer.getName());\r
+ if (gl == null)\r
+ throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName());\r
+\r
+ g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated));\r
+ }\r
+ }\r
+\r
+ class LayerChange extends ModificationAdapter {\r
+ final LayerChangeEvent event;\r
+\r
+ final GraphLayer gl;\r
+\r
+ public LayerChange(LayerChangeEvent event, GraphLayer gl) {\r
+ super(LOW_PRIORITY);\r
+ assert event != null;\r
+ assert gl != null;\r
+ this.event = event;\r
+ this.gl = gl;\r
+ }\r
+\r
+ @Override\r
+ public void perform(WriteGraph g) throws Exception {\r
+// Resource name = g.getSingleObject(gl.getLayer(), b.HasName);\r
+// g.claimValue(name, event.getSource().getName());\r
+ g.claimLiteral(gl.getLayer(), l0.HasName, event.getSource().getName(), Bindings.STRING);\r
+ }\r
+ }\r
+\r
+ void deleteTag(WriteGraph g, Resource tag) throws DatabaseException {\r
+ g.deny(tag);\r
+ }\r
+\r
+ Collection<GraphLayer> getActiveLayers(ReadGraph g) throws DatabaseException {\r
+ Collection<GraphLayer> result = new ArrayList<GraphLayer>();\r
+ for (GraphLayer gl : layers.values()) {\r
+ Boolean active = g.getPossibleRelatedValue(gl.getLayer(), dia.IsActive);\r
+ if (Boolean.TRUE.equals(active)) {\r
+ result.add(gl);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ void tagElementWithActiveLayers(WriteGraph g, Resource element) throws DatabaseException {\r
+ Collection<GraphLayer> activeLayers = getActiveLayers(g);\r
+ for (GraphLayer activeLayer : activeLayers) {\r
+ DiagramGraphUtil.tag(g, element, activeLayer.getVisible(), true);\r
+ DiagramGraphUtil.tag(g, element, activeLayer.getFocusable(), true);\r
+ }\r
+ }\r
+\r
+ GraphLayer loadLayer(ReadGraph g, Resource layer) throws DatabaseException {\r
+ String name = g.getRelatedValue(layer, l0.HasName);\r
+ Resource visible = g.getSingleObject(layer, dia.HasVisibleTag);\r
+ Resource focusable = g.getSingleObject(layer, dia.HasFocusableTag);\r
+ return new GraphLayer(name, layer, visible, focusable);\r
+ }\r
+\r
+ public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException {\r
+ SimpleLayers result = new SimpleLayers();\r
+ ConcurrentMap<String, GraphLayer> newLayers = new ConcurrentHashMap<String, GraphLayer>();\r
+\r
+ String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS);\r
+ if (fixed != null) {\r
+\r
+// for (String name : fixed) {\r
+// SimpleLayer l = new SimpleLayer(name);\r
+// result.addLayer(l);\r
+// result.activate(l);\r
+// }\r
+\r
+ // We need to put GraphLayer to newLayers so...\r
+ for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {\r
+ GraphLayer gl = loadLayer(g, layer);\r
+ for (String name : fixed) {\r
+ if (name.equals(gl.getName())) {\r
+ SimpleLayer l = new SimpleLayer(gl.getName());\r
+ newLayers.put(gl.getName(), gl);\r
+ result.addLayer(l);\r
+ result.activate(l);\r
+ }\r
+ }\r
+ }\r
+\r
+ } else {\r
+\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Loading layers");\r
+\r
+ for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {\r
+ GraphLayer gl = loadLayer(g, layer);\r
+ SimpleLayer l = new SimpleLayer(gl.getName());\r
+\r
+ newLayers.put(gl.getName(), gl);\r
+ result.addLayer(l);\r
+\r
+ Boolean active = g.getPossibleRelatedValue(layer, dia.IsActive);\r
+ if (active == null)\r
+ active = Boolean.FALSE;\r
+\r
+ if (DEBUG_LAYERS)\r
+ System.out.println(" Loaded " + (active ? "active" : "inactive") + " layer '" + gl.getName() + "'");\r
+\r
+ l.addLayerListener(layerListener);\r
+ if (active)\r
+ result.activate(l);\r
+ }\r
+\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Loaded " + newLayers.size() + " layers");\r
+\r
+ }\r
+\r
+ this.layers = newLayers;\r
+ this.layerEditor = result;\r
+ this.layerEditor.addListener(layerListener);\r
+\r
+ return result;\r
+ }\r
+\r
+ public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element)\r
+ throws DatabaseException {\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Loading layers for element " + element + " - " + e);\r
+\r
+ Set<ILayer> visible = null;\r
+ Set<ILayer> focusable = null;\r
+\r
+ for (ILayer l : layersEditor.getLayers()) {\r
+ GraphLayer gl = layers.get(l.getName());\r
+ if (gl != null) {\r
+ if (graph.hasStatement(element, gl.getVisible(), element)) {\r
+ if (visible == null)\r
+ visible = new HashSet<ILayer>(4);\r
+ visible.add(l);\r
+ if (DEBUG_LAYERS)\r
+ System.out.println(" Visible on layer '" + gl.getName() + "'");\r
+ }\r
+ if (graph.hasStatement(element, gl.getFocusable(), element)) {\r
+ if (focusable == null)\r
+ focusable = new HashSet<ILayer>(4);\r
+ focusable.add(l);\r
+ if (DEBUG_LAYERS)\r
+ System.out.println(" Focusable on layer '" + gl.getName() + "'");\r
+ }\r
+ }\r
+ }\r
+\r
+ if (visible == null)\r
+ visible = new HashSet<ILayer>(1);\r
+ if (focusable == null)\r
+ focusable = new HashSet<ILayer>(1);\r
+\r
+ e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
+ e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param layersEditor\r
+ * @param e\r
+ * @param element\r
+ * @param procedure a procedure whose exception method may be called 0 to\r
+ * many times depending on how many errors occur during layer loading\r
+ * @throws DatabaseException\r
+ */\r
+ public void loadLayersForElement(AsyncReadGraph graph, ILayersEditor layersEditor, final IElement e,\r
+ Resource element, final AsyncProcedure<IElement> callback) {\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Loading layers for element " + element + " - " + e);\r
+\r
+ final Set<ILayer> visible = new HashSet<ILayer>(2);\r
+ final Set<ILayer> focusable = new HashSet<ILayer>(2);\r
+\r
+ // NOTE: must not set layer hints into element until the layer sets have\r
+ // been properly loaded.\r
+\r
+ Set<ILayer> allLayers = layersEditor.getLayers();\r
+ if (allLayers.isEmpty()) {\r
+ e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
+ e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
+ callback.execute(graph, e);\r
+ return;\r
+ }\r
+\r
+ final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2);\r
+\r
+ for (final ILayer l : allLayers) {\r
+ final GraphLayer gl = layers.get(l.getName());\r
+ if (gl != null) {\r
+ graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter<Boolean>() {\r
+ @Override\r
+ public void execute(AsyncReadGraph graph, Boolean result) {\r
+ synchronized (visible) {\r
+ visible.add(l);\r
+ }\r
+ if (DEBUG_LAYERS)\r
+ System.out.println(" Visible on layer '" + gl.getName() + "'");\r
+ if (ready.decrementAndGet() == 0) {\r
+ e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
+ e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
+ callback.execute(graph, e);\r
+ }\r
+ }\r
+ @Override\r
+ public void exception(AsyncReadGraph graph, Throwable t) {\r
+ callback.exception(graph, t);\r
+ }\r
+ });\r
+ graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter<Boolean>() {\r
+ @Override\r
+ public void execute(AsyncReadGraph graph, Boolean result) {\r
+ synchronized (focusable) {\r
+ focusable.add(l);\r
+ }\r
+ if (DEBUG_LAYERS)\r
+ System.out.println(" Focusable on layer '" + gl.getName() + "'");\r
+ if (ready.decrementAndGet() == 0) {\r
+ e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
+ e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
+ callback.execute(graph, e);\r
+ }\r
+ }\r
+ @Override\r
+ public void exception(AsyncReadGraph graph, Throwable t) {\r
+ callback.exception(graph, t);\r
+ }\r
+ });\r
+\r
+ } else {\r
+\r
+ if (ready.addAndGet(-2) == 0) {\r
+ e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);\r
+ e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);\r
+ callback.execute(graph, e);\r
+ }\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ void putElementOnVisibleLayers(IDiagram diagram, IElement element) {\r
+ // Make the new element visible and focusable on all currently\r
+ // active layers.\r
+ ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);\r
+ if (diagramLayers != null) {\r
+ element.setHint(ElementHints.KEY_VISIBLE_LAYERS, new HashSet<ILayer>());\r
+ element.setHint(ElementHints.KEY_FOCUS_LAYERS, new HashSet<ILayer>());\r
+ Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();\r
+\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers);\r
+\r
+ for (ElementLayers elementLayers : element.getElementClass().getItemsByClass(ElementLayers.class)) {\r
+ for (ILayer layer : visibleLayers) {\r
+ elementLayers.setVisibility(element, layer, true);\r
+ elementLayers.setFocusability(element, layer, true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException {\r
+ // Make the new element visible and focusable on all currently\r
+ // active layers.\r
+ ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);\r
+ if (diagramLayers != null) {\r
+ Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();\r
+\r
+ if (DEBUG_LAYERS)\r
+ System.out.println("Marking element visible and focusable in the graph on visible layers: "\r
+ + visibleLayers);\r
+\r
+ for (ILayer layer : visibleLayers) {\r
+ GraphLayer gl = layers.get(layer.getName());\r
+ if (gl != null) {\r
+ DiagramGraphUtil.tag(g, element, gl.getVisible(), true);\r
+ DiagramGraphUtil.tag(g, element, gl.getFocusable(), true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public void removeFromAllLayers(WriteGraph graph, Resource element) throws ServiceException {\r
+ // Remove element from all layers.\r
+ graph.deny(element, dia.IsVisible);\r
+ graph.deny(element, dia.IsFocusable);\r
+ }\r
+\r
+ public GraphLayer getGraphLayer(String name) {\r
+ return layers.get(name);\r
+ }\r
+\r
+}\r