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