]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.annotation.ui/src/org/simantics/annotation/ui/SCL.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.annotation.ui / src / org / simantics / annotation / ui / SCL.java
index f4f99997c5b0e1cd0191be2d4584e065576113a6..44f9f23fd0594aafe69855de38d7c920e4774260 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2013 Association for Decentralized Information Management in\r
- * 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
- *     Semantum Oy - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.annotation.ui;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.jface.resource.ImageDescriptor;\r
-import org.eclipse.jface.viewers.ISelection;\r
-import org.simantics.Simantics;\r
-import org.simantics.annotation.ontology.AnnotationResource;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.util.URIStringUtils;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.UnaryRead;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.util.Layer0Utils;\r
-import org.simantics.db.layer0.util.RemoverUtil;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.layer0.variable.VariableBuilder;\r
-import org.simantics.db.layer0.variable.VariableMap;\r
-import org.simantics.db.layer0.variable.VariableMapImpl;\r
-import org.simantics.db.layer0.variable.Variables;\r
-import org.simantics.db.service.VirtualGraphSupport;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.modeling.ModelingResources;\r
-import org.simantics.modeling.ModelingUtils;\r
-import org.simantics.scenegraph.loader.ScenegraphLoaderUtils;\r
-import org.simantics.scenegraph.utils.NodeUtil;\r
-import org.simantics.scl.reflection.annotations.SCLValue;\r
-import org.simantics.scl.runtime.function.FunctionImpl1;\r
-import org.simantics.scl.runtime.tuple.Tuple;\r
-import org.simantics.scl.runtime.tuple.Tuple2;\r
-import org.simantics.scl.runtime.tuple.Tuple3;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.strings.AlphanumComparator;\r
-import org.simantics.utils.ui.ISelectionUtils;\r
-import org.simantics.views.swt.client.base.ISWTViewNode;\r
-import org.simantics.views.swt.client.impl.SWTExplorer;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-/**\r
- * @author Antti Villberg\r
- * @author Tuukka Lehtonen\r
- */\r
-public class SCL {\r
-    \r
-    private static final Logger LOGGER = LoggerFactory.getLogger(SCL.class);\r
-\r
-    final public static String EMPTY = "";\r
-    final public static String MAPPED = "Mapped";\r
-    final public static String SELECTED = "Selected";\r
-\r
-    final public static String NO_ANNOTATIONS = "No annotations";\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> [(String, Resource)]")\r
-    public static List<Tuple> availableSources(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {\r
-        \r
-        Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context);\r
-        if (selection == null)\r
-            return Collections.emptyList();\r
-        List<Tuple3> sources = availableSourcesImpl(graph, selection);\r
-        if (sources.isEmpty())\r
-            return Collections.emptyList();\r
-        List<Tuple> result = new ArrayList<Tuple>(sources.size());\r
-        for(Tuple3 anno : sources) {\r
-            result.add(new Tuple2(anno.get(0),""));\r
-        }\r
-        return result;\r
-        \r
-    }\r
-\r
-    /**\r
-     * Gathers available annotation sources and always returns them in the same\r
-     * order.\r
-     * \r
-     * @param graph\r
-     * @param selection\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    private static List<Tuple3> availableSourcesImpl(ReadGraph graph, Variable selection) throws DatabaseException {\r
-        List<Variable> vars = gatherSourceVariables(graph, selection);\r
-        if (vars.isEmpty())\r
-            return Collections.emptyList();\r
-        int size = vars.size();\r
-        ArrayList<Tuple3> result = new ArrayList<Tuple3>(size);\r
-        result.add(new Tuple3(sourceLabel(graph, vars.get(0)), SELECTED, vars.get(0)));\r
-        for (int i = 1; i < size; ++i) {\r
-            Variable v = vars.get(i);\r
-            result.add(new Tuple3(sourceLabel(graph, v), MAPPED, v));\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Gathers variables starting from source in a particular order each time.\r
-     * First configuration components and second diagram elements.\r
-     * \r
-     * @param graph\r
-     * @param source\r
-     * @return\r
-     * @throws DatabaseException\r
-     */\r
-    private static List<Variable> gatherSourceVariables(ReadGraph graph, Variable source) throws DatabaseException {\r
-        Resource represents = source.getPossibleRepresents(graph);\r
-        if (represents == null)\r
-            return Collections.singletonList(source);\r
-\r
-        ModelingResources MOD = ModelingResources.getInstance(graph);\r
-        ArrayList<Variable> result = new ArrayList<Variable>(4);\r
-        for (Resource r : ModelingUtils.getElementCorrespondendences(graph, represents))\r
-            addPossibleVariable(graph, r, result);\r
-        for(Resource r : graph.getObjects(represents, MOD.DiagramToComposite))\r
-            addPossibleVariable(graph, r, result);\r
-        result.add(source);\r
-        for(Resource r : graph.getObjects(represents, MOD.ComponentToElement))\r
-            addPossibleVariable(graph, r, result);\r
-        for(Resource r : graph.getObjects(represents, MOD.CompositeToDiagram))\r
-            addPossibleVariable(graph, r, result);\r
-        return result;\r
-    }\r
-\r
-    private static void addPossibleVariable(ReadGraph graph, Resource r, List<Variable> result) throws DatabaseException {\r
-        Variable v = Variables.getPossibleVariable(graph, r);\r
-        if (v != null)\r
-            result.add(v);\r
-    }\r
-\r
-    private static String sourceLabel(ReadGraph graph, Variable variable) throws DatabaseException {\r
-        Resource represents = variable.getPossibleRepresents(graph);\r
-        if(represents != null) {\r
-            DiagramResource DIA = DiagramResource.getInstance(graph);\r
-            if(graph.isInstanceOf(represents, DIA.Diagram)) return "Diagram";\r
-            else if(graph.isInstanceOf(represents, DIA.Flag)) return "Flag Element";\r
-            else if(graph.isInstanceOf(represents, DIA.RouteGraphConnection)) return "Connection Element";\r
-            else if(graph.isInstanceOf(represents, DIA.Monitor)) return "Monitor Element";\r
-            else if(graph.isInstanceOf(represents, DIA.Element)) return "Diagram Element";\r
-            else return variable.getName(graph);\r
-        } else {\r
-            return variable.getURI(graph);\r
-        }\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    private static <T> T selectedSource(ReadGraph graph, Variable selection) throws DatabaseException {\r
-\r
-        String name = selectedSourceName(graph, selection);\r
-        for(Tuple tuple : availableSourcesImpl(graph, selection)) {\r
-            if(tuple.get(1).equals(name)) return (T)tuple.get(2);\r
-        }\r
-        \r
-        return null;\r
-        \r
-    }\r
-    \r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")\r
-    public static String selectedSource(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-        String name = selectedSourceName(graph, selection);\r
-        for(Tuple tuple : availableSourcesImpl(graph, selection)) {\r
-            if(tuple.get(1).equals(name)) return (String)tuple.get(0);\r
-        }\r
-        \r
-        return EMPTY;\r
-\r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object selectedSourceModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        return new FunctionImpl1<Object, Object>() {\r
-\r
-            @Override\r
-            public Object apply(Object sourceKey) {\r
-                try {\r
-                    Session s = Simantics.getSession();\r
-                    VirtualGraph vg = s.getService(VirtualGraphSupport.class).getWorkspacePersistent("preferences");\r
-                    s.syncRequest(new SetDefaultAnnotationSourceRequest(vg, context, sourceKey));\r
-                } catch (DatabaseException e) {\r
-                    Activator.getDefault().getLog().log(\r
-                            new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to update default annotation source, see exception for details.", e));\r
-                }\r
-                return null;\r
-            }\r
-\r
-        };\r
-\r
-    }\r
-\r
-    private static class SetDefaultAnnotationSourceRequest extends WriteRequest {\r
-\r
-        private Variable context;\r
-        private Object sourceKey;\r
-\r
-        public SetDefaultAnnotationSourceRequest(VirtualGraph vg, Variable context, Object sourceKey) {\r
-            super(vg);\r
-            this.context = context;\r
-            this.sourceKey = sourceKey;\r
-        }\r
-\r
-        @Override\r
-        public void perform(WriteGraph graph) throws DatabaseException {\r
-            AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-            Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-            Resource model = Variables.getPossibleIndexRoot(graph, selection);\r
-\r
-            List<Tuple3> annos = availableSourcesImpl(graph, selection);\r
-            for(Tuple3 anno : annos) {\r
-                if(anno.get(0).equals(sourceKey)) {\r
-                    graph.claimLiteral(model, ANNO.DefaultAnnotationSource, (String)anno.get(1), Bindings.STRING);\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-\r
-    }\r
-\r
-    private static final Comparator<? super Tuple3> AVAILABLE_ANNOTATION_SORTER = new Comparator<Tuple3>() {\r
-        @Override\r
-        public int compare(Tuple3 o1, Tuple3 o2) {\r
-            String s1 = (String) o1.c2;\r
-            String s2 = (String) o2.c2;\r
-            return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(s1, s2);\r
-        }\r
-    };\r
-\r
-    /**\r
-     * @param graph\r
-     * @param selection\r
-     * @return list of (Variable annotation, Resource annotation, String name) tuples\r
-     * @throws DatabaseException\r
-     */\r
-    private static Collection<Tuple3> availableAnnotationsImpl(ReadGraph graph, Variable selection) throws DatabaseException {\r
-\r
-        Layer0 L0 = Layer0.getInstance(graph);\r
-        AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-\r
-        selection = selectedSource(graph, selection);\r
-        \r
-        ArrayList<Tuple3> result = new ArrayList<Tuple3>();\r
-        for (Variable child : selection.getChildren(graph)) {\r
-            Resource represents = child.getPossibleRepresents(graph);\r
-            if (represents != null && graph.isInstanceOf(represents, ANNO.Annotation)) {\r
-                String name = graph.getPossibleRelatedValue(represents, L0.HasName);\r
-                if (name != null)\r
-                    result.add(new Tuple3(child, represents, name));\r
-            }\r
-        }\r
-        for (Variable property : selection.getProperties(graph)) {\r
-            Resource propertyResource = property.getPossibleRepresents(graph);\r
-            if (propertyResource != null && graph.isInstanceOf(propertyResource, ANNO.Annotation)) {\r
-                String propertyName = property.getName(graph);\r
-                result.add(new Tuple3(property, propertyResource, propertyName));\r
-            }\r
-        }\r
-\r
-        // Sort returned annotations by annotation property name to keep the results stable.\r
-        Collections.sort(result, AVAILABLE_ANNOTATION_SORTER);\r
-\r
-        return result;\r
-\r
-    }\r
-\r
-    private static Pair<Resource, String> defaultAnnotationTypeAndName(ReadGraph graph, Variable selection) throws DatabaseException {\r
-        AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-        Resource model = Variables.getPossibleIndexRoot(graph, selection);\r
-        Resource type = graph.getPossibleObject(model, ANNO.HasDefaultAnnotationType);\r
-        String name = graph.getPossibleRelatedValue(model, ANNO.HasDefaultAnnotationName, Bindings.STRING);\r
-        return Pair.make(type, name);\r
-    }\r
-\r
-    private static String selectedAnnotationName(ReadGraph graph, Variable selection) throws DatabaseException {\r
-        Pair<Resource, String> typeAndName = defaultAnnotationTypeAndName(graph, selection);\r
-        Collection<Tuple3> available = availableAnnotationsImpl(graph, selection);\r
-        if (!available.isEmpty()) {\r
-            if (available.size() == 1 || typeAndName.first == null)\r
-                return (String) available.iterator().next().c2;\r
-            String firstTypeMatch = null;\r
-            for (Tuple3 anno : available) {\r
-                if (graph.isInstanceOf((Resource) anno.c1, typeAndName.first)) {\r
-                    if (firstTypeMatch == null)\r
-                        firstTypeMatch = (String) anno.c2;\r
-                    if (typeAndName.second != null && typeAndName.second.equals(anno.c2)) {\r
-                        // Ok, it just doesn't match better than this.\r
-                        return (String) anno.c2;\r
-                    }\r
-                }\r
-            }\r
-            if (firstTypeMatch != null)\r
-                return firstTypeMatch;\r
-            // Nothing => return the first one from list\r
-            return (String)available.iterator().next().c2;\r
-        }\r
-        return NO_ANNOTATIONS;\r
-    }\r
-\r
-    private static String selectedSourceName(ReadGraph graph, Variable selection) throws DatabaseException {\r
-    \r
-        AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-        Resource model = Variables.getPossibleIndexRoot(graph, selection);\r
-        if (model == null)\r
-            return EMPTY;\r
-        String name = graph.getPossibleRelatedValue(model, ANNO.DefaultAnnotationSource, Bindings.STRING);\r
-        if(name != null) {\r
-            for(Tuple tuple : availableSourcesImpl(graph, selection)) {\r
-                if(tuple.get(1).equals(name)) return name;\r
-            }\r
-        }\r
-\r
-        Set<String> available = new THashSet<String>();\r
-        for(Tuple tuple : availableSourcesImpl(graph, selection)) available.add((String)tuple.get(1));\r
-        if(available.isEmpty()) return EMPTY;\r
-        \r
-        if(available.contains(MAPPED)) return MAPPED;\r
-        else return available.iterator().next();\r
-    \r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object explorerInput(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-        String selected = selectedAnnotationName(graph, selection);\r
-        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {\r
-            if(selected.equals(anno.c2)) {\r
-                return anno.c0;\r
-            }\r
-        }\r
-        return null;\r
-\r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")\r
-    public static String descriptionText(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-       String result = "";\r
-       Variable sel = getSelectedAnnotationVariable(graph, context);\r
-       if(sel != null) {\r
-\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-            Resource literal = sel.getPossibleRepresents(graph);\r
-            if(literal != null) {\r
-               Resource container = graph.getPossibleObject(literal, L0.PartOf);\r
-               if(container != null) {\r
-                       Resource model = Variables.getPossibleIndexRoot(graph, sel);\r
-                       String modelURI = graph.getURI(model);\r
-                       String path = graph.getURI(literal);\r
-                       if(path.startsWith(modelURI)) path = path.substring(modelURI.length()+1);\r
-                    result += URIStringUtils.unescape(path); \r
-                } else {\r
-                       result += "The annotation is not attached to a library";\r
-                }\r
-            }\r
-            \r
-       }\r
-       \r
-       return result;\r
-\r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object explorerInput2(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        return ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-\r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> [(String, Resource)]")\r
-    public static List<Tuple> availableAnnotations(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {\r
-        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-        ArrayList<Tuple> result = new ArrayList<Tuple>();\r
-        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {\r
-            result.add(new Tuple2(anno.c2, anno.c1));\r
-        }\r
-        if(result.isEmpty()) result.add(new Tuple2(NO_ANNOTATIONS, ""));\r
-        return result;\r
-    }\r
-\r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")\r
-    public static String selectedAnnotation(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        final Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-        return selectedAnnotationName(graph, selection);\r
-\r
-    }\r
-    \r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object selectedAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-        return new FunctionImpl1<Object, Object>() {\r
-\r
-            @Override\r
-            public Object apply(final Object _key) {\r
-\r
-                Session s = Simantics.getSession();\r
-                VirtualGraph vg = s.getService(VirtualGraphSupport.class).getWorkspacePersistent("preferences");\r
-                s.async(new WriteRequest(vg) {\r
-\r
-                    @Override\r
-                    public void perform(WriteGraph graph) throws DatabaseException {\r
-                        AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-                        String key = (String)_key;\r
-                        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-                        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {\r
-                            if(key.equals(anno.c2)) {\r
-                                Resource type = graph.getPossibleType((Resource) anno.c1, ANNO.Annotation);\r
-                                Resource model = Variables.getPossibleIndexRoot(graph, selection);\r
-                                graph.deny(model, ANNO.HasDefaultAnnotationType);\r
-                                graph.claim(model, ANNO.HasDefaultAnnotationType, type);\r
-                                graph.denyValue(model, ANNO.HasDefaultAnnotationName);\r
-                                graph.claimLiteral(model, ANNO.HasDefaultAnnotationName, key, Bindings.STRING);\r
-                                break;\r
-                            }\r
-                        }\r
-                    }\r
-\r
-                });\r
-\r
-                return null;\r
-\r
-            }\r
-\r
-        };\r
-\r
-    }\r
-    \r
-    private static Resource getSelectionResource(Variable context) throws DatabaseException {\r
-        return Simantics.getSession().syncRequest(new UnaryRead<Variable,Resource>(context) {\r
-\r
-            @Override\r
-            public Resource perform(ReadGraph graph) throws DatabaseException {\r
-                Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, parameter);\r
-                Variable source = selectedSource(graph, selection);\r
-                return source.getPossibleRepresents(graph);\r
-            }\r
-            \r
-        });\r
-    }\r
-\r
-    static class AddModifier extends FunctionImpl1<Object, Object> {\r
-       \r
-       private final Variable context;\r
-       \r
-       public AddModifier(Variable context) {\r
-               this.context = context;\r
-       }\r
-       \r
-       private void doAdd(final Variable variable) throws DatabaseException {\r
-               \r
-               if(variable != null) {\r
-                       // We have a selected annotation\r
-                AnnotationUtils.newAnnotation(variable);\r
-               } else {\r
-                       // No annotation selected\r
-                Resource parent = getSelectionResource(context);\r
-                if(parent != null)\r
-                    AnnotationUtils.newAnnotation(parent);\r
-               }\r
-\r
-       }\r
-\r
-               @Override\r
-               public Object apply(Object p0) {\r
-                       \r
-                       ISWTViewNode node = (ISWTViewNode)p0;\r
-                       SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");\r
-                       if(properties == null) return null;\r
-                       try {\r
-                               doAdd((Variable)properties.input);\r
-                       } catch (DatabaseException e) {\r
-                           LOGGER.error("newAnnotationModifier failed", e);\r
-                       }\r
-                       return null;\r
-                       \r
-               }\r
-       \r
-    }\r
-    \r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object newAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-       return new AddModifier(context);\r
-    }\r
-\r
-    private static Variable getSelectedAnnotationVariable(ReadGraph graph, Variable context) throws DatabaseException {\r
-        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);\r
-               String selected = selectedAnnotationName(graph, selection);\r
-        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {\r
-               if(anno.c2.equals(selected)) {\r
-                       return (Variable)anno.c0;\r
-               }\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object removeAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-       return new RemoveModifier();\r
-       \r
-    }\r
-\r
-    public static class SaveModifier extends FunctionImpl1<Object, Object> {\r
-       \r
-       private boolean doSave(final Variable variable) {\r
-               \r
-               if(!AnnotationUtils.isAnnotation(variable)) return false;\r
-               \r
-                       final Map<Resource, Pair<String, ImageDescriptor>> map = AnnotationUtils.findLibraries(variable);\r
-                       if(map == null) return false;\r
-\r
-               AnnotationUtils.queryLibrary(map, selected -> {\r
-                   Simantics.getSession().async(new WriteRequest() {\r
-                       @Override\r
-                       public void perform(WriteGraph graph) throws DatabaseException {\r
-                           Resource represents = variable.getPossibleRepresents(graph);\r
-                           if(represents != null && !selected.second.isEmpty()) {\r
-                               saveAnnotation(graph, represents, selected.first, selected.second);     \r
-                           }\r
-                       }\r
-                   });\r
-               });\r
-\r
-               return true;\r
-       }\r
-       \r
-       public static Resource saveAnnotation(WriteGraph graph, Resource annotation, Resource library, String name) throws DatabaseException {\r
-               \r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-               if(graph.hasStatement(annotation, L0.PartOf)) {\r
-                       graph.deny(annotation, L0.PartOf);\r
-               }\r
-               graph.claim(library, L0.ConsistsOf, L0.PartOf, annotation);\r
-               graph.claimLiteral(annotation, L0.HasName, name, Bindings.STRING);\r
-                       \r
-               return annotation;\r
-       }\r
-       \r
-\r
-               @Override\r
-               public Object apply(Object p0) {\r
-                       \r
-                       ISWTViewNode node = (ISWTViewNode)p0;\r
-                       SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");\r
-                       if(properties == null) return null;\r
-                       ISelection selection = properties.lastSelection;\r
-                       if(selection == null || selection.isEmpty()) {\r
-                               doSave((Variable)properties.input);\r
-                               return null;\r
-                       }\r
-                       \r
-                       Collection<Variable> vars = ISelectionUtils.filterSetSelection(selection, Variable.class);\r
-                       if(vars.size() != 1) return null;\r
-\r
-               Variable selected = vars.iterator().next();\r
-               if(!doSave(selected))\r
-                       doSave((Variable)properties.input);\r
-                       \r
-                       return null;\r
-                       \r
-               }\r
-       \r
-    }\r
-    \r
-    static class RemoveModifier extends FunctionImpl1<Object, Object> {\r
-\r
-       private boolean doRemove(final Variable variable) {\r
-\r
-               if(!AnnotationUtils.isAnnotation(variable))\r
-                       return false;\r
-\r
-               Simantics.getSession().async(new WriteRequest() {\r
-                       @Override\r
-                       public void perform(WriteGraph graph) throws DatabaseException {\r
-                               graph.markUndoPoint();\r
-                               Resource represents = variable.getPossibleRepresents(graph);\r
-                               if(represents != null) {\r
-                                       Layer0 L0 = Layer0.getInstance(graph);\r
-                        AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-                                       if(graph.isInstanceOf(represents, ANNO.Annotation)) {\r
-                                               Resource subject = variable.getParent(graph).getRepresents(graph);\r
-                                               Resource predicate = variable.getPossiblePredicateResource(graph);\r
-                                               if(predicate != null) {\r
-                                                       // This is a property annotation (no entry) - unlink\r
-                                               graph.deny(subject, predicate);\r
-                                               Layer0Utils.addCommentMetadata(graph, "Unlinked a property annotation " + graph.getRelatedValue2(predicate, L0.HasName, Bindings.STRING) + " " + predicate.toString() + " from " + graph.getRelatedValue2(subject, L0.HasName, Bindings.STRING) + " " + subject.toString());\r
-                                               } else {\r
-                                                       // This should be an entry annotation - remove from container\r
-                                                       graph.deny(subject, ANNO.Annotation_HasEntry, represents);\r
-                                                       Layer0Utils.addCommentMetadata(graph, "Removed an entry annotation " + graph.getRelatedValue2(subject, L0.HasName, Bindings.STRING) + " " + subject.toString() + " from its container " + graph.getRelatedValue2(represents, L0.HasName, Bindings.STRING));\r
-                                               }\r
-                                               // If the annotation is not in any library remove it\r
-                                               if(!graph.hasStatement(represents, L0.PartOf))\r
-                                                       RemoverUtil.remove(graph, represents);\r
-                                       }\r
-                               }\r
-\r
-                       }\r
-                       \r
-               });\r
-\r
-               return true;\r
-       }\r
-\r
-       @Override\r
-       public Object apply(Object p0) {\r
-\r
-               ISWTViewNode node = (ISWTViewNode)p0;\r
-               SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");\r
-               if(properties == null) return null;\r
-               ISelection selection = properties.lastSelection;\r
-               if(selection == null || selection.isEmpty()) {\r
-                       doRemove((Variable)properties.input);\r
-                       return null;\r
-               }\r
-\r
-               Collection<Variable> vars = ISelectionUtils.filterSetSelection(selection, Variable.class);\r
-               if(vars.size() != 1) return null;\r
-\r
-               Variable selected = vars.iterator().next();\r
-               if(!doRemove(selected))\r
-                       doRemove((Variable)properties.input);\r
-\r
-               return null;\r
-\r
-       }\r
-\r
-    }\r
-    \r
-    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")\r
-    public static Object saveAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {\r
-\r
-       return new SaveModifier();\r
-\r
-    }\r
-\r
-    @SCLValue(type = "VariableMap")\r
-       public static VariableMap domainChildren = new VariableMapImpl() {\r
-\r
-       private Map<String,Resource> children(ReadGraph graph, Resource resource) throws DatabaseException {\r
-               AnnotationResource ANNO = AnnotationResource.getInstance(graph);\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-               Collection<Resource> objects = graph.getObjects(resource, ANNO.Annotation_HasEntry); \r
-               THashMap<String, Resource> result = new THashMap<String, Resource>(objects.size());\r
-               for(Resource r : objects) {\r
-                       String name = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);\r
-                       if(name != null) {\r
-                               if (result.put(name, r) != null)\r
-                                   LOGGER.error("The database contains siblings with the same name " + name + " (resource=$" + resource.getResourceId() +").");\r
-                       }\r
-               }\r
-               return result;\r
-       }\r
-       \r
-               @Override\r
-               public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {\r
-               Map<String, Resource> children = children(graph,context.getRepresents(graph));\r
-               Resource child = children.get(name);\r
-               if(child == null) return null;\r
-                       VariableBuilder variableBuilder = graph.adapt(child, VariableBuilder.class);\r
-                       return variableBuilder.buildChild(graph, context, null, child);\r
-               }\r
-\r
-               @Override\r
-               public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {\r
-                       Map<String,Resource> childMap = children(graph,context.getRepresents(graph));\r
-                       if(childMap.isEmpty()) return map;\r
-                       if(map == null) map = new THashMap<String,Variable>();\r
-               for(Map.Entry<String, Resource> entry : childMap.entrySet()) {\r
-                   String name = entry.getKey();\r
-                   Resource child = entry.getValue();\r
-                       VariableBuilder variableBuilder = graph.adapt(child, VariableBuilder.class);\r
-                       Variable var = variableBuilder.buildChild(graph, context, null, child);\r
-                   if(var != null) {\r
-                       map.put(name, var);\r
-                   } else {\r
-                       System.err.println("No adapter for " + child + " in " + context.getURI(graph));\r
-                   }\r
-               }\r
-               return map;\r
-               }\r
-               \r
-       };\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2013 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:
+ *     Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.annotation.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.simantics.Simantics;
+import org.simantics.annotation.ontology.AnnotationResource;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.util.URIStringUtils;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.util.RemoverUtil;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.VariableBuilder;
+import org.simantics.db.layer0.variable.VariableMap;
+import org.simantics.db.layer0.variable.VariableMapImpl;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.modeling.ModelingUtils;
+import org.simantics.scenegraph.loader.ScenegraphLoaderUtils;
+import org.simantics.scenegraph.utils.NodeUtil;
+import org.simantics.scl.reflection.annotations.SCLValue;
+import org.simantics.scl.runtime.function.FunctionImpl1;
+import org.simantics.scl.runtime.tuple.Tuple;
+import org.simantics.scl.runtime.tuple.Tuple2;
+import org.simantics.scl.runtime.tuple.Tuple3;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.strings.AlphanumComparator;
+import org.simantics.utils.ui.ISelectionUtils;
+import org.simantics.views.swt.client.base.ISWTViewNode;
+import org.simantics.views.swt.client.impl.SWTExplorer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * @author Antti Villberg
+ * @author Tuukka Lehtonen
+ */
+public class SCL {
+    
+    private static final Logger LOGGER = LoggerFactory.getLogger(SCL.class);
+
+    final public static String EMPTY = "";
+    final public static String MAPPED = "Mapped";
+    final public static String SELECTED = "Selected";
+
+    final public static String NO_ANNOTATIONS = "No annotations";
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> [(String, Resource)]")
+    public static List<Tuple> availableSources(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
+        
+        Variable selection = ScenegraphLoaderUtils.getPossibleVariableSelection(graph, context);
+        if (selection == null)
+            return Collections.emptyList();
+        List<Tuple3> sources = availableSourcesImpl(graph, selection);
+        if (sources.isEmpty())
+            return Collections.emptyList();
+        List<Tuple> result = new ArrayList<Tuple>(sources.size());
+        for(Tuple3 anno : sources) {
+            result.add(new Tuple2(anno.get(0),""));
+        }
+        return result;
+        
+    }
+
+    /**
+     * Gathers available annotation sources and always returns them in the same
+     * order.
+     * 
+     * @param graph
+     * @param selection
+     * @return
+     * @throws DatabaseException
+     */
+    private static List<Tuple3> availableSourcesImpl(ReadGraph graph, Variable selection) throws DatabaseException {
+        List<Variable> vars = gatherSourceVariables(graph, selection);
+        if (vars.isEmpty())
+            return Collections.emptyList();
+        int size = vars.size();
+        ArrayList<Tuple3> result = new ArrayList<Tuple3>(size);
+        result.add(new Tuple3(sourceLabel(graph, vars.get(0)), SELECTED, vars.get(0)));
+        for (int i = 1; i < size; ++i) {
+            Variable v = vars.get(i);
+            result.add(new Tuple3(sourceLabel(graph, v), MAPPED, v));
+        }
+        return result;
+    }
+
+    /**
+     * Gathers variables starting from source in a particular order each time.
+     * First configuration components and second diagram elements.
+     * 
+     * @param graph
+     * @param source
+     * @return
+     * @throws DatabaseException
+     */
+    private static List<Variable> gatherSourceVariables(ReadGraph graph, Variable source) throws DatabaseException {
+        Resource represents = source.getPossibleRepresents(graph);
+        if (represents == null)
+            return Collections.singletonList(source);
+
+        ModelingResources MOD = ModelingResources.getInstance(graph);
+        ArrayList<Variable> result = new ArrayList<Variable>(4);
+        for (Resource r : ModelingUtils.getElementCorrespondendences(graph, represents))
+            addPossibleVariable(graph, r, result);
+        for(Resource r : graph.getObjects(represents, MOD.DiagramToComposite))
+            addPossibleVariable(graph, r, result);
+        result.add(source);
+        for(Resource r : graph.getObjects(represents, MOD.ComponentToElement))
+            addPossibleVariable(graph, r, result);
+        for(Resource r : graph.getObjects(represents, MOD.CompositeToDiagram))
+            addPossibleVariable(graph, r, result);
+        return result;
+    }
+
+    private static void addPossibleVariable(ReadGraph graph, Resource r, List<Variable> result) throws DatabaseException {
+        Variable v = Variables.getPossibleVariable(graph, r);
+        if (v != null)
+            result.add(v);
+    }
+
+    private static String sourceLabel(ReadGraph graph, Variable variable) throws DatabaseException {
+        Resource represents = variable.getPossibleRepresents(graph);
+        if(represents != null) {
+            DiagramResource DIA = DiagramResource.getInstance(graph);
+            if(graph.isInstanceOf(represents, DIA.Diagram)) return "Diagram";
+            else if(graph.isInstanceOf(represents, DIA.Flag)) return "Flag Element";
+            else if(graph.isInstanceOf(represents, DIA.RouteGraphConnection)) return "Connection Element";
+            else if(graph.isInstanceOf(represents, DIA.Monitor)) return "Monitor Element";
+            else if(graph.isInstanceOf(represents, DIA.Element)) return "Diagram Element";
+            else return variable.getName(graph);
+        } else {
+            return variable.getURI(graph);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T selectedSource(ReadGraph graph, Variable selection) throws DatabaseException {
+
+        String name = selectedSourceName(graph, selection);
+        for(Tuple tuple : availableSourcesImpl(graph, selection)) {
+            if(tuple.get(1).equals(name)) return (T)tuple.get(2);
+        }
+        
+        return null;
+        
+    }
+    
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")
+    public static String selectedSource(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+        String name = selectedSourceName(graph, selection);
+        for(Tuple tuple : availableSourcesImpl(graph, selection)) {
+            if(tuple.get(1).equals(name)) return (String)tuple.get(0);
+        }
+        
+        return EMPTY;
+
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object selectedSourceModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        return new FunctionImpl1<Object, Object>() {
+
+            @Override
+            public Object apply(Object sourceKey) {
+                try {
+                    Session s = Simantics.getSession();
+                    VirtualGraph vg = s.getService(VirtualGraphSupport.class).getWorkspacePersistent("preferences");
+                    s.syncRequest(new SetDefaultAnnotationSourceRequest(vg, context, sourceKey));
+                } catch (DatabaseException e) {
+                    Activator.getDefault().getLog().log(
+                            new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to update default annotation source, see exception for details.", e));
+                }
+                return null;
+            }
+
+        };
+
+    }
+
+    private static class SetDefaultAnnotationSourceRequest extends WriteRequest {
+
+        private Variable context;
+        private Object sourceKey;
+
+        public SetDefaultAnnotationSourceRequest(VirtualGraph vg, Variable context, Object sourceKey) {
+            super(vg);
+            this.context = context;
+            this.sourceKey = sourceKey;
+        }
+
+        @Override
+        public void perform(WriteGraph graph) throws DatabaseException {
+            AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+            Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+            Resource model = Variables.getPossibleIndexRoot(graph, selection);
+
+            List<Tuple3> annos = availableSourcesImpl(graph, selection);
+            for(Tuple3 anno : annos) {
+                if(anno.get(0).equals(sourceKey)) {
+                    graph.claimLiteral(model, ANNO.DefaultAnnotationSource, (String)anno.get(1), Bindings.STRING);
+                    break;
+                }
+            }
+        }
+
+    }
+
+    private static final Comparator<? super Tuple3> AVAILABLE_ANNOTATION_SORTER = new Comparator<Tuple3>() {
+        @Override
+        public int compare(Tuple3 o1, Tuple3 o2) {
+            String s1 = (String) o1.c2;
+            String s2 = (String) o2.c2;
+            return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(s1, s2);
+        }
+    };
+
+    /**
+     * @param graph
+     * @param selection
+     * @return list of (Variable annotation, Resource annotation, String name) tuples
+     * @throws DatabaseException
+     */
+    private static Collection<Tuple3> availableAnnotationsImpl(ReadGraph graph, Variable selection) throws DatabaseException {
+
+        Layer0 L0 = Layer0.getInstance(graph);
+        AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+
+        selection = selectedSource(graph, selection);
+        
+        ArrayList<Tuple3> result = new ArrayList<Tuple3>();
+        for (Variable child : selection.getChildren(graph)) {
+            Resource represents = child.getPossibleRepresents(graph);
+            if (represents != null && graph.isInstanceOf(represents, ANNO.Annotation)) {
+                String name = graph.getPossibleRelatedValue(represents, L0.HasName);
+                if (name != null)
+                    result.add(new Tuple3(child, represents, name));
+            }
+        }
+        for (Variable property : selection.getProperties(graph)) {
+            Resource propertyResource = property.getPossibleRepresents(graph);
+            if (propertyResource != null && graph.isInstanceOf(propertyResource, ANNO.Annotation)) {
+                String propertyName = property.getName(graph);
+                result.add(new Tuple3(property, propertyResource, propertyName));
+            }
+        }
+
+        // Sort returned annotations by annotation property name to keep the results stable.
+        Collections.sort(result, AVAILABLE_ANNOTATION_SORTER);
+
+        return result;
+
+    }
+
+    private static Pair<Resource, String> defaultAnnotationTypeAndName(ReadGraph graph, Variable selection) throws DatabaseException {
+        AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+        Resource model = Variables.getPossibleIndexRoot(graph, selection);
+        Resource type = graph.getPossibleObject(model, ANNO.HasDefaultAnnotationType);
+        String name = graph.getPossibleRelatedValue(model, ANNO.HasDefaultAnnotationName, Bindings.STRING);
+        return Pair.make(type, name);
+    }
+
+    private static String selectedAnnotationName(ReadGraph graph, Variable selection) throws DatabaseException {
+        Pair<Resource, String> typeAndName = defaultAnnotationTypeAndName(graph, selection);
+        Collection<Tuple3> available = availableAnnotationsImpl(graph, selection);
+        if (!available.isEmpty()) {
+            if (available.size() == 1 || typeAndName.first == null)
+                return (String) available.iterator().next().c2;
+            String firstTypeMatch = null;
+            for (Tuple3 anno : available) {
+                if (graph.isInstanceOf((Resource) anno.c1, typeAndName.first)) {
+                    if (firstTypeMatch == null)
+                        firstTypeMatch = (String) anno.c2;
+                    if (typeAndName.second != null && typeAndName.second.equals(anno.c2)) {
+                        // Ok, it just doesn't match better than this.
+                        return (String) anno.c2;
+                    }
+                }
+            }
+            if (firstTypeMatch != null)
+                return firstTypeMatch;
+            // Nothing => return the first one from list
+            return (String)available.iterator().next().c2;
+        }
+        return NO_ANNOTATIONS;
+    }
+
+    private static String selectedSourceName(ReadGraph graph, Variable selection) throws DatabaseException {
+    
+        AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+        Resource model = Variables.getPossibleIndexRoot(graph, selection);
+        if (model == null)
+            return EMPTY;
+        String name = graph.getPossibleRelatedValue(model, ANNO.DefaultAnnotationSource, Bindings.STRING);
+        if(name != null) {
+            for(Tuple tuple : availableSourcesImpl(graph, selection)) {
+                if(tuple.get(1).equals(name)) return name;
+            }
+        }
+
+        Set<String> available = new THashSet<String>();
+        for(Tuple tuple : availableSourcesImpl(graph, selection)) available.add((String)tuple.get(1));
+        if(available.isEmpty()) return EMPTY;
+        
+        if(available.contains(MAPPED)) return MAPPED;
+        else return available.iterator().next();
+    
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object explorerInput(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+        String selected = selectedAnnotationName(graph, selection);
+        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {
+            if(selected.equals(anno.c2)) {
+                return anno.c0;
+            }
+        }
+        return null;
+
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")
+    public static String descriptionText(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+       String result = "";
+       Variable sel = getSelectedAnnotationVariable(graph, context);
+       if(sel != null) {
+
+               Layer0 L0 = Layer0.getInstance(graph);
+            Resource literal = sel.getPossibleRepresents(graph);
+            if(literal != null) {
+               Resource container = graph.getPossibleObject(literal, L0.PartOf);
+               if(container != null) {
+                       Resource model = Variables.getPossibleIndexRoot(graph, sel);
+                       String modelURI = graph.getURI(model);
+                       String path = graph.getURI(literal);
+                       if(path.startsWith(modelURI)) path = path.substring(modelURI.length()+1);
+                    result += URIStringUtils.unescape(path); 
+                } else {
+                       result += "The annotation is not attached to a library";
+                }
+            }
+            
+       }
+       
+       return result;
+
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object explorerInput2(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        return ScenegraphLoaderUtils.getVariableSelection(graph, context);
+
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> [(String, Resource)]")
+    public static List<Tuple> availableAnnotations(ReadGraph graph, Resource resource, Variable context) throws DatabaseException {
+        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+        ArrayList<Tuple> result = new ArrayList<Tuple>();
+        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {
+            result.add(new Tuple2(anno.c2, anno.c1));
+        }
+        if(result.isEmpty()) result.add(new Tuple2(NO_ANNOTATIONS, ""));
+        return result;
+    }
+
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")
+    public static String selectedAnnotation(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        final Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+        return selectedAnnotationName(graph, selection);
+
+    }
+    
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object selectedAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+        return new FunctionImpl1<Object, Object>() {
+
+            @Override
+            public Object apply(final Object _key) {
+
+                Session s = Simantics.getSession();
+                VirtualGraph vg = s.getService(VirtualGraphSupport.class).getWorkspacePersistent("preferences");
+                s.async(new WriteRequest(vg) {
+
+                    @Override
+                    public void perform(WriteGraph graph) throws DatabaseException {
+                        AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+                        String key = (String)_key;
+                        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+                        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {
+                            if(key.equals(anno.c2)) {
+                                Resource type = graph.getPossibleType((Resource) anno.c1, ANNO.Annotation);
+                                Resource model = Variables.getPossibleIndexRoot(graph, selection);
+                                graph.deny(model, ANNO.HasDefaultAnnotationType);
+                                graph.claim(model, ANNO.HasDefaultAnnotationType, type);
+                                graph.denyValue(model, ANNO.HasDefaultAnnotationName);
+                                graph.claimLiteral(model, ANNO.HasDefaultAnnotationName, key, Bindings.STRING);
+                                break;
+                            }
+                        }
+                    }
+
+                });
+
+                return null;
+
+            }
+
+        };
+
+    }
+    
+    private static Resource getSelectionResource(Variable context) throws DatabaseException {
+        return Simantics.getSession().syncRequest(new UnaryRead<Variable,Resource>(context) {
+
+            @Override
+            public Resource perform(ReadGraph graph) throws DatabaseException {
+                Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, parameter);
+                Variable source = selectedSource(graph, selection);
+                return source.getPossibleRepresents(graph);
+            }
+            
+        });
+    }
+
+    static class AddModifier extends FunctionImpl1<Object, Object> {
+       
+       private final Variable context;
+       
+       public AddModifier(Variable context) {
+               this.context = context;
+       }
+       
+       private void doAdd(final Variable variable) throws DatabaseException {
+               
+               if(variable != null) {
+                       // We have a selected annotation
+                AnnotationUtils.newAnnotation(variable);
+               } else {
+                       // No annotation selected
+                Resource parent = getSelectionResource(context);
+                if(parent != null)
+                    AnnotationUtils.newAnnotation(parent);
+               }
+
+       }
+
+               @Override
+               public Object apply(Object p0) {
+                       
+                       ISWTViewNode node = (ISWTViewNode)p0;
+                       SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");
+                       if(properties == null) return null;
+                       try {
+                               doAdd((Variable)properties.input);
+                       } catch (DatabaseException e) {
+                           LOGGER.error("newAnnotationModifier failed", e);
+                       }
+                       return null;
+                       
+               }
+       
+    }
+    
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object newAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+       return new AddModifier(context);
+    }
+
+    private static Variable getSelectedAnnotationVariable(ReadGraph graph, Variable context) throws DatabaseException {
+        Variable selection = ScenegraphLoaderUtils.getVariableSelection(graph, context);
+               String selected = selectedAnnotationName(graph, selection);
+        for(Tuple3 anno : availableAnnotationsImpl(graph, selection)) {
+               if(anno.c2.equals(selected)) {
+                       return (Variable)anno.c0;
+               }
+        }
+        return null;
+    }
+    
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object removeAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+       return new RemoveModifier();
+       
+    }
+
+    public static class SaveModifier extends FunctionImpl1<Object, Object> {
+       
+       private boolean doSave(final Variable variable) {
+               
+               if(!AnnotationUtils.isAnnotation(variable)) return false;
+               
+                       final Map<Resource, Pair<String, ImageDescriptor>> map = AnnotationUtils.findLibraries(variable);
+                       if(map == null) return false;
+
+               AnnotationUtils.queryLibrary(map, selected -> {
+                   Simantics.getSession().async(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                           Resource represents = variable.getPossibleRepresents(graph);
+                           if(represents != null && !selected.second.isEmpty()) {
+                               saveAnnotation(graph, represents, selected.first, selected.second);     
+                           }
+                       }
+                   });
+               });
+
+               return true;
+       }
+       
+       public static Resource saveAnnotation(WriteGraph graph, Resource annotation, Resource library, String name) throws DatabaseException {
+               
+               Layer0 L0 = Layer0.getInstance(graph);
+               if(graph.hasStatement(annotation, L0.PartOf)) {
+                       graph.deny(annotation, L0.PartOf);
+               }
+               graph.claim(library, L0.ConsistsOf, L0.PartOf, annotation);
+               graph.claimLiteral(annotation, L0.HasName, name, Bindings.STRING);
+                       
+               return annotation;
+       }
+       
+
+               @Override
+               public Object apply(Object p0) {
+                       
+                       ISWTViewNode node = (ISWTViewNode)p0;
+                       SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");
+                       if(properties == null) return null;
+                       ISelection selection = properties.lastSelection;
+                       if(selection == null || selection.isEmpty()) {
+                               doSave((Variable)properties.input);
+                               return null;
+                       }
+                       
+                       Collection<Variable> vars = ISelectionUtils.filterSetSelection(selection, Variable.class);
+                       if(vars.size() != 1) return null;
+
+               Variable selected = vars.iterator().next();
+               if(!doSave(selected))
+                       doSave((Variable)properties.input);
+                       
+                       return null;
+                       
+               }
+       
+    }
+    
+    static class RemoveModifier extends FunctionImpl1<Object, Object> {
+
+       private boolean doRemove(final Variable variable) {
+
+               if(!AnnotationUtils.isAnnotation(variable))
+                       return false;
+
+               Simantics.getSession().async(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                               graph.markUndoPoint();
+                               Resource represents = variable.getPossibleRepresents(graph);
+                               if(represents != null) {
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                        AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+                                       if(graph.isInstanceOf(represents, ANNO.Annotation)) {
+                                               Resource subject = variable.getParent(graph).getRepresents(graph);
+                                               Resource predicate = variable.getPossiblePredicateResource(graph);
+                                               if(predicate != null) {
+                                                       // This is a property annotation (no entry) - unlink
+                                               graph.deny(subject, predicate);
+                                               Layer0Utils.addCommentMetadata(graph, "Unlinked a property annotation " + graph.getRelatedValue2(predicate, L0.HasName, Bindings.STRING) + " " + predicate.toString() + " from " + graph.getRelatedValue2(subject, L0.HasName, Bindings.STRING) + " " + subject.toString());
+                                               } else {
+                                                       // This should be an entry annotation - remove from container
+                                                       graph.deny(subject, ANNO.Annotation_HasEntry, represents);
+                                                       Layer0Utils.addCommentMetadata(graph, "Removed an entry annotation " + graph.getRelatedValue2(subject, L0.HasName, Bindings.STRING) + " " + subject.toString() + " from its container " + graph.getRelatedValue2(represents, L0.HasName, Bindings.STRING));
+                                               }
+                                               // If the annotation is not in any library remove it
+                                               if(!graph.hasStatement(represents, L0.PartOf))
+                                                       RemoverUtil.remove(graph, represents);
+                                       }
+                               }
+
+                       }
+                       
+               });
+
+               return true;
+       }
+
+       @Override
+       public Object apply(Object p0) {
+
+               ISWTViewNode node = (ISWTViewNode)p0;
+               SWTExplorer properties = (SWTExplorer)NodeUtil.browsePossible(node, "./Properties");
+               if(properties == null) return null;
+               ISelection selection = properties.lastSelection;
+               if(selection == null || selection.isEmpty()) {
+                       doRemove((Variable)properties.input);
+                       return null;
+               }
+
+               Collection<Variable> vars = ISelectionUtils.filterSetSelection(selection, Variable.class);
+               if(vars.size() != 1) return null;
+
+               Variable selected = vars.iterator().next();
+               if(!doRemove(selected))
+                       doRemove((Variable)properties.input);
+
+               return null;
+
+       }
+
+    }
+    
+    @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
+    public static Object saveAnnotationModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException {
+
+       return new SaveModifier();
+
+    }
+
+    @SCLValue(type = "VariableMap")
+       public static VariableMap domainChildren = new VariableMapImpl() {
+
+       private Map<String,Resource> children(ReadGraph graph, Resource resource) throws DatabaseException {
+               AnnotationResource ANNO = AnnotationResource.getInstance(graph);
+               Layer0 L0 = Layer0.getInstance(graph);
+               Collection<Resource> objects = graph.getObjects(resource, ANNO.Annotation_HasEntry); 
+               THashMap<String, Resource> result = new THashMap<String, Resource>(objects.size());
+               for(Resource r : objects) {
+                       String name = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);
+                       if(name != null) {
+                               if (result.put(name, r) != null)
+                                   LOGGER.error("The database contains siblings with the same name " + name + " (resource=$" + resource.getResourceId() +").");
+                       }
+               }
+               return result;
+       }
+       
+               @Override
+               public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
+               Map<String, Resource> children = children(graph,context.getRepresents(graph));
+               Resource child = children.get(name);
+               if(child == null) return null;
+                       VariableBuilder variableBuilder = graph.adapt(child, VariableBuilder.class);
+                       return variableBuilder.buildChild(graph, context, null, child);
+               }
+
+               @Override
+               public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
+                       Map<String,Resource> childMap = children(graph,context.getRepresents(graph));
+                       if(childMap.isEmpty()) return map;
+                       if(map == null) map = new THashMap<String,Variable>();
+               for(Map.Entry<String, Resource> entry : childMap.entrySet()) {
+                   String name = entry.getKey();
+                   Resource child = entry.getValue();
+                       VariableBuilder variableBuilder = graph.adapt(child, VariableBuilder.class);
+                       Variable var = variableBuilder.buildChild(graph, context, null, child);
+                   if(var != null) {
+                       map.put(name, var);
+                   } else {
+                       System.err.println("No adapter for " + child + " in " + context.getURI(graph));
+                   }
+               }
+               return map;
+               }
+               
+       };
+
+}