]> gerrit.simantics Code Review - simantics/district.git/commitdiff
New element selector configuration dialog 78/4078/2
authorReino Ruusu <reino.ruusu@semantum.fi>
Thu, 26 Mar 2020 14:17:42 +0000 (16:17 +0200)
committerReino Ruusu <reino.ruusu@semantum.fi>
Mon, 30 Mar 2020 13:49:03 +0000 (16:49 +0300)
Includes element type selection and property selection from drop-down
menus. Also includes some refactoring of the underlying selector code.

gitlab #84

Change-Id: If5e42a7962d6d17b97a9fb6a3d207ed860858b8c

Remove block of commented-out code

gitlab #84

Change-Id: Ia75eeb9e9a2a4dc5a339244837cd6e6f01f97891

Layout adjustments in element selector dialog.

gitlab #84

Change-Id: I28de9ca5e1bc22eef607126ec75dcd3a93263188

More rigorous content validation for element selection dialog

gitlab #84

Change-Id: Iadb875ea9db3db8350095e21c39ecfad6cc6af36

Remove misplaced pop-up menu command

gitlab #84

Change-Id: I4412390556daacc4172c34018fa9d0fea65bf586

Rename element selection view in UI to "District Finder"

gitlab #84

Change-Id: I668eb468c169ccd8dfd8575daff9c1f1a0f01f2d

Manage drop-down menu contents by context in district finder dialog

gitlab #84

Also fix failure in creation of new queries.

Change-Id: I122ec005119a7efd8878b24815dc09a226e8b6d9

Fix use of static ontology variables out of context

gitlab #84

Change-Id: I94d17685f44e4bbfdaccaf9b3bfabefacb4e1c8e

Fix validation of selection by property when not used

gitlab #84

Change-Id: I6fd1b5d297ac2a07536edcd51a5f1b1099e3d06d

org.simantics.district.selection.ui/META-INF/MANIFEST.MF
org.simantics.district.selection.ui/fragment.e4xmi
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/EditSelectorDialog.java
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/ElementSelectionView.java
org.simantics.district.selection/graph/DiagramElementSelection.pgraph
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionResource.java
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java

index 2dc51ac9a53126a01d90878d7c3f1d8e075a6c51..86962fc5f8a3e5317708444c10b553c38ee15954 100644 (file)
@@ -29,7 +29,9 @@ Require-Bundle: javax.inject,
  org.simantics.district.network.ui,
  org.simantics.structural.ontology,
  org.simantics.diagram.ontology,
- org.eclipse.e4.core.services
+ org.eclipse.e4.core.services,
+ org.simantics.district.network.ontology;bundle-version="1.0.0",
+ org.simantics.layer0.utils
 Bundle-ManifestVersion: 2
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Vendor: Semantum oy
index 3b6a9456ce87b439f575ecf769e14018869bb111..b58825a1f0b2d4f394e0d058dbf87a251761d12c 100644 (file)
@@ -1,15 +1,12 @@
 <?xml version="1.0" encoding="ASCII"?>
 <fragment:ModelFragments xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/descriptor/basic" xmlns:commands="http://www.eclipse.org/ui/2010/UIModel/application/commands" xmlns:fragment="http://www.eclipse.org/ui/2010/UIModel/fragment" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_BxaXACerEeWxCPrV0pAZQQ">
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_EI7oACerEeWxCPrV0pAZQQ" featurename="descriptors" parentElementId="xpath:/">
-    <elements xsi:type="basic:PartDescriptor" xmi:id="_KMklcCerEeWxCPrV0pAZQQ" elementId="org.simantics.district.selection.ui.parts.elementSelectionView" label="Element Selection Queries" iconURI="platform:/plugin/org.simantics.silk.ontology/graph/images/table_go.png" tooltip="" category="District Network" closeable="true" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.parts.ElementSelectionView">
+    <elements xsi:type="basic:PartDescriptor" xmi:id="_KMklcCerEeWxCPrV0pAZQQ" elementId="org.simantics.district.selection.ui.parts.elementSelectionView" label="District Finder" iconURI="platform:/plugin/org.simantics.silk.ontology/graph/images/table_go.png" tooltip="" category="District Network" closeable="true" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.parts.ElementSelectionView">
       <tags>View</tags>
       <tags>categoryTag:District</tags>
     </elements>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_aD4EkCVaEem-kMBxWCWPIg" featurename="menuContributions" parentElementId="xpath:/">
-    <elements xsi:type="menu:MenuContribution" xmi:id="_zFdSsCVaEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.menucontribution.openview" parentId="popup:#AprosDiagramPopup">
-      <children xsi:type="menu:HandledMenuItem" xmi:id="_403skCVaEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.handledmanuitem.openview" label="Open Diagram Element Selection View" iconURI="platform:/plugin/com.famfamfam.silk/icons/table_key.png" command="_OdOfoCVcEem-kMBxWCWPIg"/>
-    </elements>
     <elements xsi:type="menu:MenuContribution" xmi:id="_QOvjwCVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.menucontribution.selectionView.contextMenu" parentId="org.simantics.district.selection.ui.contextMenu">
       <children xsi:type="menu:DirectMenuItem" xmi:id="_bjEA4OnSEemDQq3ncfg9zg" elementId="org.simantics.district.selection.ui.directmenuitem.performselection" label="Perform Selection" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.handlers.PerformSelectionQueryHandler"/>
       <children xsi:type="menu:HandledMenuItem" xmi:id="_ZKWM0CVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.contextMenu.createNewSelection" label="Create New Selection" iconURI="platform:/plugin/com.famfamfam.silk/icons/table_add.png" command="_rH7MsCVgEem-kMBxWCWPIg"/>
@@ -18,7 +15,7 @@
     </elements>
   </fragments>
   <fragments xsi:type="fragment:StringModelFragment" xmi:id="_Sy-NUCVbEem-kMBxWCWPIg" featurename="commands" parentElementId="xpath:/">
-    <elements xsi:type="commands:Command" xmi:id="_OdOfoCVcEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.command.openview" commandName="Open Diagram Element Selection Table"/>
+    <elements xsi:type="commands:Command" xmi:id="_OdOfoCVcEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.command.openview" commandName="Open District Finder"/>
     <elements xsi:type="commands:Command" xmi:id="_rH7MsCVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.command.createNewSelection" commandName="Create New Selection"/>
     <elements xsi:type="commands:Command" xmi:id="_dL1xIC7IEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.command.editElementSelector" commandName="Edit Element Selection"/>
     <elements xsi:type="commands:Command" xmi:id="_RfOU0C7PEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.command.deleteElementSelector" commandName="Delete Element Selection"/>
index b68c3fe11148504c43348c540b0b21ea51f2cb1b..074cdfeb0118e0e24343c71a8c15cf9468aae318 100644 (file)
@@ -2,23 +2,28 @@ package org.simantics.district.selection.ui.parts;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.RowDataFactory;
 import org.eclipse.jface.layout.RowLayoutFactory;
-import org.eclipse.jface.window.Window;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StackLayout;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.widgets.Button;
@@ -28,21 +33,23 @@ import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
 import org.simantics.Simantics;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
-import org.simantics.db.Session;
 import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
 import org.simantics.db.common.request.ReadRequest;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.exception.RuntimeDatabaseException;
 import org.simantics.db.layer0.QueryIndexUtils;
 import org.simantics.db.layer0.request.ActiveModels;
+import org.simantics.db.layer0.request.PropertyInfo;
+import org.simantics.db.layer0.request.PropertyInfoRequest;
 import org.simantics.db.layer0.util.Layer0Utils;
-import org.simantics.db.request.Read;
-import org.simantics.db.request.WriteResult;
 import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.region.ontology.DiagramRegionsResource;
 import org.simantics.district.route.ontology.RouteResource;
 import org.simantics.district.selection.ElementSelectionResource;
@@ -62,13 +69,21 @@ import org.simantics.district.selection.ElementSelector.RegionCondition;
 import org.simantics.district.selection.ElementSelector.RouteCondition;
 import org.simantics.district.selection.ElementSelector.Selector;
 import org.simantics.layer0.Layer0;
+import org.simantics.layer0.utils.direct.GraphUtils;
 import org.simantics.modeling.ModelingResources;
+import org.simantics.structural.stubs.StructuralResource2;
 import org.simantics.utils.datastructures.Arrays;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.ui.BundleUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class EditSelectorDialog extends Dialog {
 
+       private static final RowLayoutFactory ROW_LAYOUT = RowLayoutFactory.fillDefaults().wrap(false);
+       private static final ImageDescriptor CROSS_IMAGE = BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png");
+       private static final ImageDescriptor PLUS_IMAGE = BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/add.png");
+
        private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
        
        private ElementSelector elementSelector;
@@ -77,13 +92,22 @@ public class EditSelectorDialog extends Dialog {
        Collection<Resource> currentSelection;
 
        // Data for comboboxes
-       Map<Resource, String> diagrams;
+       private List<Resource> diagrams;
+       private ArrayList<String> diagramNames;
        
        private String[] regionNames;
        private Resource[] regionResources;
 
        private String[] routeNames;
        private Resource[] routeResources;
+       
+       private List<Resource> componentTypes;
+       private List<String> componentTypeNames;
+       
+       private List<String> propertyNames;
+       private List<String> propertyLabels;
+       
+       private Composite conditionPanel;
 
        // Dialog fields
        private int generatorIndex;
@@ -98,19 +122,43 @@ public class EditSelectorDialog extends Dialog {
        private int selectorIndex;
        private Combo selectorField;
 
+       private Resource componentType;
+       private Combo componentTypeField;
+       
        private String propertyName;
-       private Text propertyField;
+       private Combo propertyField;
 
        private int numberOfItems;
        private Text nField;
 
        private Condition condition;
-       private Button removeConditionButton;
-       private Text conditionLabel;
 
        // Dialog area component
        private Composite content;
 
+       private int diagramIndex;
+
+       private LocalResourceManager resourceManager;
+       
+       static class ValidationException extends Exception {
+               private static final long serialVersionUID = 1L;
+
+               public ValidationException(String message) {
+                       super(message);
+               }
+       }
+       
+       // Function type for updating condition objects with optional validation
+       static interface Updater {
+               // If 'validate' is true, a runtime exception may be thrown for invalid values
+               void update(boolean validate) throws ValidationException;
+       }
+       
+       final static Updater NULL_UPDATE = validate -> {};
+       
+       // Called to read values from controls into conditions
+       Updater updater = NULL_UPDATE;
+
        @Inject
        public EditSelectorDialog(Shell shell, ElementSelector elementSelector, Collection<Resource> currentSelection) {
                super(shell);
@@ -132,38 +180,17 @@ public class EditSelectorDialog extends Dialog {
                
                this.currentSelection = currentSelection;
                
-               final Map<Resource, String> regions = new HashMap<>();
-               final Map<Resource, String> routes = new HashMap<>();
-               
-               try {
-                       Simantics.getSession().syncRequest(new Read<Void>() {
-                               @Override
-                               public Void perform(ReadGraph graph) throws DatabaseException {
-                                       Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
-                                       List<Resource> regionCollection = QueryIndexUtils.searchByType(graph, model, DiagramRegionsResource.getInstance(graph).Region);
-                                       for (Resource r : regionCollection) {
-                                               String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
-                                               regions.put(r, name);
-                                       }
-                                       
-                                       List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, RouteResource.getInstance(graph).Route);
-                                       for (Resource r : routeCollection) {
-                                               String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
-                                               routes.put(r, name);
-                                       }
-                                       return null;
-                               }
+               Map<Resource, String> diagramMap = ElementSelector.findDiagrams();
+               diagrams = new ArrayList<Resource>(diagramMap.size());
+               diagramNames = new ArrayList<String>(diagramMap.size());
+               diagramMap.entrySet()
+                       .stream()
+                       .sorted(Comparator.comparing(e -> e.getValue()))
+                       .forEachOrdered(e -> {
+                               diagrams.add(e.getKey());
+                               diagramNames.add(e.getValue());
                        });
-               } catch (DatabaseException e) {
-                       LOGGER.error("Failed to read routes and/or regions in the model", e);
-               }
-               
-               regionNames = regions.values().toArray(new String[regions.size()]);
-               regionResources = regions.keySet().toArray(new Resource[regions.size()]);
                
-               routeNames = routes.values().toArray(new String[routes.size()]);
-               routeResources = routes.keySet().toArray(new Resource[routes.size()]);
-       
                name = elementSelector != null ? elementSelector.getName() : "";
                propertyName = "";
                numberOfItems = 1;
@@ -183,7 +210,6 @@ public class EditSelectorDialog extends Dialog {
                        }
                        else if (generator instanceof ExplicitGenerator) {
                                generatorIndex = 2;
-                               // TODO: Management of explicit lists of elements
                        }
                        else {
                                throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
@@ -204,30 +230,168 @@ public class EditSelectorDialog extends Dialog {
                        }
                        
                        condition = elementSelector.getCondition();
+                       
+                       componentType = elementSelector.getSelector().componentType;
+               }
+               
+               readRegions(diagram);
+               readRoutes();
+               readComponentTypes();
+               updatePropertyList();
+       }
+
+       private void readComponentTypes() {
+               try {
+                       Simantics.getSession().syncRequest(new ReadRequest() {
+                               @Override
+                               public void run(ReadGraph graph) throws DatabaseException {
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       List<Resource> types = findComponentTypes(graph);
+                                       
+                                       componentTypes = new ArrayList<>(types.size() + 1);
+                                       componentTypeNames = new ArrayList<>(types.size() + 1);
+                                       
+                                       componentTypes.add(null);
+                                       componentTypeNames.add("Any type");
+                                       componentTypes.addAll(types);
+                                       for (Resource t : types) {
+                                               componentTypeNames.add(graph.getValue2(t, L0.HasName));
+                                       }
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read district component types", e);
+               }
+       }
+
+       private void readRoutes() {
+               final Map<Resource, String> routes = new HashMap<>();
+               
+               try {
+                       Simantics.getSession().syncRequest(new ReadRequest() {
+                               @Override
+                               public void run(ReadGraph graph) throws DatabaseException {
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       RouteResource ROUTE = RouteResource.getInstance(graph);
+                                       
+                                       Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
+                                       List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, ROUTE.Route);
+                                       for (Resource r : routeCollection) {
+                                               String name = graph.getRelatedValue(r, L0.HasLabel);
+                                               routes.put(r, name);
+                                       }
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read routes in the model", e);
                }
+               
+               routeNames = routes.values().toArray(new String[routes.size()]);
+               routeResources = routes.keySet().toArray(new Resource[routes.size()]);
+       }
+
+       private void readRegions(Resource diagram) {
+               final Map<Resource, String> regions = new HashMap<>();
+               
+               try {
+                       Simantics.getSession().syncRequest(new ReadRequest() {
+                               @Override
+                               public void run(ReadGraph graph) throws DatabaseException {
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       ModelingResources MOD = ModelingResources.getInstance(graph);
+                                       DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
+                                       
+                                       // If a specific diagram is given, use that
+                                       Collection<Resource> ds = diagram != null ? Collections.singleton(diagram) : diagrams;
+                                       
+                                       for (Resource composite : ds) {
+                                               Resource diagram = graph.getSingleObject(composite, MOD.CompositeToDiagram);
+                                               for (Resource r : graph.getObjects(diagram, DR.hasRegion)) {
+                                                       if (!graph.isInstanceOf(r, DR.Region))
+                                                               continue;
+                                                       String name = graph.getRelatedValue(r, L0.HasLabel);
+                                                       regions.put(r, name);
+                                               }
+                                       }
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read regions in the model", e);
+               }
+               
+               regionNames = regions.values().toArray(new String[regions.size()]);
+               regionResources = regions.keySet().toArray(new Resource[regions.size()]);
        }
        
+       private void updateDialog() {
+               try {
+                       updater.update(false);
+               } catch (ValidationException e) {
+                       // Should not happend with argument false
+                       assert(false);
+               }
+               
+               updater = updateConditionPanel();
+               
+               content.layout(true, true);
+               getShell().pack();
+       }
+
        @Override
        protected void okPressed() {
-               generatorIndex = sourceField.getSelectionIndex();
-               if (generatorIndex == 1) {
-                       int selectionIndex = diagramField.getSelectionIndex();
-                       if (selectionIndex < 0) {
-                               ErrorDialog.openError(getShell(), "Error", "Please select a diagram", new Status(IStatus.ERROR, "org.simantics.district.selection.ui", "No diagram selected"));
-                               return;
+               try {
+                       generatorIndex = sourceField.getSelectionIndex();
+                       if (generatorIndex == 1) {
+                               int selectionIndex = diagramField.getSelectionIndex();
+                               if (selectionIndex < 0) {
+                                       diagramField.setFocus();
+                                       throw new ValidationException("Please select a diagram");
+                               }
+                               
+                               diagram = diagrams.get(selectionIndex);
+                       }
+                       
+                       name = nameField.getText();
+                       if (name.isEmpty()) {
+                               nameField.setFocus();
+                               throw new ValidationException("Please enter a name");
+                       }
+                       
+                       componentType = componentTypes.get(componentTypeField.getSelectionIndex());
+                       
+                       selectorIndex = selectorField.getSelectionIndex();
+                       if (selectorIndex != 0) {
+                               int propertyIndex = propertyField.getSelectionIndex();
+                               propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
+                               if (propertyName.isEmpty()) {
+                                       propertyField.setFocus();
+                                       throw new ValidationException("Please select a property");
+                               }
                        }
                        
-                       diagram = new ArrayList<Resource>(diagrams.keySet()).get(selectionIndex);
+                       // Try to parse number of items
+                       if (useNumberOfItems()) {
+                               try {
+                                       numberOfItems = Integer.parseInt(nField.getText());
+                                       if (numberOfItems <= 0) {
+                                               nField.selectAll();
+                                               nField.setFocus();
+                                               throw new ValidationException("Number of elements must be positive");
+                                       }
+                               } catch (NumberFormatException e) {
+                                       nField.selectAll();
+                                       nField.setFocus();
+                                       throw new ValidationException("Please enter a valid number of elements");
+                               }
+                       }
+       
+                       // To to update condition definitions
+                       updater.update(true);
+               } catch (ValidationException e) {
+                       MessageDialog.openError(this.getShell(), "Missing data", e.getMessage());
+                       return;
                }
                
-               name = nameField.getText();
-               
-               selectorIndex = selectorField.getSelectionIndex();
-               
-               propertyName = propertyField.getText();
-               String text = nField.getText();
-               numberOfItems = "".equals(text) ? 0 : Integer.parseInt(text);
-               
                super.okPressed();
        }
        
@@ -306,6 +470,9 @@ public class EditSelectorDialog extends Dialog {
                                }
                                graph.claim(selector, L0.InstanceOf, selectorType);
                                graph.claim(selection, ES.Selection_HasSelector, selector);
+                               graph.deny(selector, ES.Selector_HasMapping);
+                               if (componentType != null)
+                                       graph.claim(selector, ES.Selector_HasMapping, componentType);
                                
                                if (selectorIndex > 0) {
                                        graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
@@ -314,19 +481,30 @@ public class EditSelectorDialog extends Dialog {
                                
                                // Condition
                                if (condition != null) {
-                                       graph.claim(selection, ES.Selection_HasCondition, condition.resource);
+                                       Resource conditionResource = condition.update(graph);
+                                       graph.claim(selection, ES.Selection_HasCondition, conditionResource);
                                }
                        }
                });
        }
 
+       private boolean isDiagramFieldVisible() {
+               return generatorIndex == 1;
+       }
+
+       private boolean useNumberOfItems() {
+               return selectorIndex != 0;
+       }
+
        @Override
        protected Control createDialogArea(Composite parent) {
+               this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
+               
                // Set dialog title
                getShell().setText("Edit element selector");
                
                content = new Composite(parent, SWT.NONE);
-               GridLayoutFactory.swtDefaults().numColumns(3).applyTo(content);
+               GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
                
                // Name
                Label nameLabel = new Label(content, SWT.NONE);
@@ -336,599 +514,513 @@ public class EditSelectorDialog extends Dialog {
                nameField = new Text(content, SWT.BORDER);
                nameField.setEditable(true);
                nameField.setText(name);
-               GridDataFactory.swtDefaults().span(2, 1).hint(200, SWT.DEFAULT).applyTo(nameField);
+               GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
+               
+               // Selector
+               Label selectorLabel = new Label(content, SWT.NONE);
+               selectorLabel.setText("Select");
+               GridDataFactory.swtDefaults().applyTo(selectorLabel);
+
+               Composite selectorComposite = new Composite(content, SWT.NONE);
+               GridDataFactory.swtDefaults().applyTo(selectorComposite);
+               RowLayoutFactory.fillDefaults().applyTo(selectorComposite);
+               
+               nField = new Text(selectorComposite, SWT.BORDER);
+               RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
+               if (useNumberOfItems())
+                       nField.setText(Integer.toString(numberOfItems));
+               nField.setEnabled(useNumberOfItems());
+               
+               componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
+               RowDataFactory.swtDefaults().applyTo(componentTypeField);
+               componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
+               {
+                       int index = componentTypes.indexOf(componentType);
+                       componentTypeField.select(index >= 0 ? index : 0);
+               }
+               
+               // Update property selection controls when component type changes
+               componentTypeField.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               int index = componentTypeField.getSelectionIndex();
+                               componentType = index >= 0 ? componentTypes.get(index) : null;
+                               updatePropertyList();
+                               propertyField.setItems(propertyLabels.toArray(new String[] {}));
+                               
+                               updateDialog();
+                       }
+               });
+               
+               new Label(selectorComposite, SWT.NONE).setText("with");
+               
+               selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
+               selectorField.setItems("All", "Lowest", "Highest");
+               selectorField.select(selectorIndex);
+               RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
+               
+               propertyField = new Combo(selectorComposite, SWT.NONE);
+               RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyField);
+               propertyField.setItems(propertyLabels.toArray(new String[] {}));
+               {
+                       int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
+                       if (index >= 0)
+                               propertyField.select(index);
+                       else
+                               propertyField.setText(propertyName != null ? propertyName : "");
+               }
+               propertyField.setEnabled(useNumberOfItems());
+               
+               selectorField.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               selectorIndex = selectorField.getSelectionIndex();
+                               
+                               boolean enable = useNumberOfItems();
+                               nField.setEnabled(enable);
+                               propertyField.setEnabled(enable);
+                               
+                               nField.setText(enable ? Integer.toString(numberOfItems) : "");
+                       }
+               });
                
                // Source
                Label sourceLabel = new Label(content, SWT.NONE);
-               sourceLabel.setText("Source");
-               GridDataFactory.swtDefaults().applyTo(sourceLabel);
+               sourceLabel.setText("from");
+               GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
+               
+               Composite sourceComposite = new Composite(content, SWT.NONE);
+               GridDataFactory.swtDefaults().applyTo(sourceComposite);
+               RowLayoutFactory.fillDefaults().applyTo(sourceComposite);
                
-               sourceField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
-               sourceField.setItems("Model", "Diagram", "Current selection");
+               sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
+               RowDataFactory.swtDefaults().applyTo(sourceField);
+               sourceField.setItems("Whole model", "Diagram", "Current selection");
                sourceField.select(generatorIndex);
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(sourceField);
                
-               diagramField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(diagramField);
-               diagrams = ElementSelector.findDiagrams();
-               diagramField.setItems(diagrams.values().toArray(new String[diagrams.size()]));
+               diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
+               RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(diagramField);
+               diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
+               diagramField.setEnabled(isDiagramFieldVisible());
                
-               int diagramIndex = diagram != null ? new ArrayList<>(diagrams.keySet()).indexOf(diagram) : -1;
+               diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
                diagramField.select(diagramIndex);
                
                sourceField.addSelectionListener(new SelectionAdapter() {
                        @Override
                        public void widgetSelected(SelectionEvent e) {
                                generatorIndex = sourceField.getSelectionIndex();
-                               diagramField.setVisible(isDiagramFieldVisible());
+                               boolean enabled = isDiagramFieldVisible();
+                               if (!enabled) {
+                                       diagramIndex = diagramField.getSelectionIndex();
+                                       diagramField.clearSelection();
+                               } else {
+                                       if (diagramIndex >= 0)
+                                               diagramField.select(diagramIndex);
+                                       else
+                                               diagramField.clearSelection();
+                               }
+                               diagramField.setEnabled(enabled);
+
+                               // Refresh list of regions for current diagram
+                               diagram = enabled ? (diagramIndex >= 0 ? diagrams.get(diagramIndex) : null) : null;
+                               readRegions(diagram);
+                               updateDialog();
                        }
                });
                
                sourceField.select(generatorIndex);
-               diagramField.setVisible(isDiagramFieldVisible());
                
-               // Selector
-               Label selectorLabel = new Label(content, SWT.NONE);
-               selectorLabel.setText("Select");
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorLabel);
+               // Condition
+               Label label = new Label(content, SWT.NONE);
+               GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
+               label.setText("where");
                
-               selectorField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
-               selectorField.setItems("All", "N lowest", "N highest");
-               selectorField.select(selectorIndex);
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorField);
+               conditionPanel = new Composite(content, SWT.NONE);
+               GridDataFactory.swtDefaults().span(1, 2).minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
+               GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
                
-               Composite selectorComposite = new Composite(content, SWT.NONE);
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorComposite);
-               GridLayoutFactory.swtDefaults().numColumns(2).applyTo(selectorComposite);
+               updater = updateConditionPanel();
                
-               Label propertyLabel = new Label(selectorComposite, SWT.NONE);
-               propertyLabel.setText("Property name");
-               GridDataFactory.swtDefaults().applyTo(propertyLabel);
+               return content;
+       }
+       
+       private Updater updateConditionPanel() {
+               // Erase contents
+               for (Widget c : conditionPanel.getChildren())
+                       c.dispose();
                
-               propertyField = new Text(selectorComposite, SWT.BORDER);
-               propertyField.setText(propertyName);
-               GridDataFactory.swtDefaults().hint(80, SWT.DEFAULT).applyTo(propertyField);
+               return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
+       }
+
+       private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
+               // Create new contents
+               Button notCheck = new Button(parent, SWT.CHECK);
+               GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
+               notCheck.setText("not");
+               notCheck.setSelection(condition != null && condition.isInverse);
                
-               Label nLabel = new Label(selectorComposite, SWT.NONE);
-               nLabel.setText("Number of elements");
-               GridDataFactory.swtDefaults().applyTo(nLabel);
+               Composite conditionComp = new Composite(parent, SWT.NONE);
+               GridDataFactory.fillDefaults().applyTo(conditionComp);
                
-               nField = new Text(selectorComposite, SWT.BORDER);
-               nField.setText(Integer.toString(numberOfItems));
-               GridDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
+               Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
+               typeCombo.setItems(
+                               "No condition",
+                               "Property",
+                               "In region",
+                               "On route",
+                               "All of",
+                               "Any of"
+                       );
                
-               selectorField.addSelectionListener(new SelectionAdapter() {
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                               selectorIndex = selectorField.getSelectionIndex();
-                               selectorComposite.setVisible(isSelectorCompositeVisible());
+               typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
+
+               final Updater updater;
+               if (condition instanceof PropertyCondition) {
+                       typeCombo.select(1);
+                       updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
+               } else if (condition instanceof RegionCondition) {
+                       typeCombo.select(2);
+                       updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
+               } else if (condition instanceof RouteCondition) {
+                       typeCombo.select(3);
+                       updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
+               } else if (condition instanceof AggregateCondition) {
+                       AggregateCondition cond = (AggregateCondition) condition;
+                       typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
+                       updater = createAggregateConditionPanel(conditionComp, cond);
+               } else {
+                       ROW_LAYOUT.applyTo(conditionComp);
+                       notCheck.setEnabled(false);
+                       typeCombo.select(0);
+                       return NULL_UPDATE;
+               }
+               
+               return validate -> {
+                       updater.update(validate);
+                       condition.isInverse = notCheck.getSelection();
+               };
+       }
+       
+       private final class ConditionTypeSelectionListener extends SelectionAdapter {
+               private final Combo typeCombo;
+               private final Consumer<Condition> consumer;
+               private final Condition finalCondition;
+       
+               private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
+                       this.typeCombo = typeCombo;
+                       this.consumer = consumer;
+                       this.finalCondition = finalCondition;
+               }
+       
+               @Override
+               public void widgetSelected(SelectionEvent e) {
+                       int index = typeCombo.getSelectionIndex();
+                       Condition newCondition = finalCondition;
+                       switch (index) {
+                       case 0:
+                               newCondition = null;
+                               break;
+                       case 1:
+                               newCondition = createPropertyCondition("", null, null);
+                               break;
+                       case 2:
+                               newCondition = createRegionCondition(null);
+                               break;
+                       case 3:
+                               newCondition = createRouteCondition(null);
+                               break;
+                       case 4:
+                               if (newCondition instanceof AggregateCondition)
+                                       ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
+                               else
+                                       newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
+                               break;
+                       case 5:
+                               if (newCondition instanceof AggregateCondition)
+                                       ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
+                               else
+                                       newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
+                               break;
                        }
-               });
                
-               selectorField.select(selectorIndex);
-               selectorComposite.setVisible(isSelectorCompositeVisible());
+                       consumer.accept(newCondition);
+                       
+                       updateDialog();
+               }
+       }
+
+       private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
+               GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
+               new Label(conditionComp, SWT.NONE); // Eat extra column
                
-               // Condition
-               new Label(content, SWT.NONE).setText("Condition");
-               conditionLabel = new Text(content, SWT.READ_ONLY);
-               GridDataFactory.swtDefaults().span(2, 1).applyTo(conditionLabel);
-               
-               new Label(content, SWT.NONE);
-               Composite conditionPanel = new Composite(content, SWT.NONE);
-               GridDataFactory.swtDefaults().span(2, 1).applyTo(conditionPanel);
-               GridLayoutFactory.swtDefaults().margins(0, 0).numColumns(2).applyTo(conditionPanel);
-               Button conditionButton = new Button(conditionPanel, SWT.PUSH);
-               conditionButton.setText("Edit...");
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(conditionButton);
-               removeConditionButton = new Button(conditionPanel, SWT.PUSH);
-               removeConditionButton.setText("Remove");
-               GridDataFactory.swtDefaults().span(1, 1).applyTo(removeConditionButton);
-               
-               updateCondition();
-               
-               conditionButton.addSelectionListener(new SelectionAdapter() {
+               int n = cond.conditions.size();
+               final Updater[] updates = new Updater[n];
+               for (int i = 0; i < n; i++) {
+                       updates[i] = createConditionRowPanel(conditionComp, cond, i);
+               }
+               
+               Button addButton = new Button(conditionComp, SWT.PUSH);
+               GridDataFactory.swtDefaults().applyTo(addButton);
+               addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
+               
+               addButton.addSelectionListener(new SelectionAdapter() {
                        @Override
                        public void widgetSelected(SelectionEvent e) {
-                               ConditionDialog dialog = new ConditionDialog(getShell(), condition);
-                               if (dialog.open() == Window.OK) {
-                                       try {
-                                               condition = dialog.createCondition();
-                                       } catch (DatabaseException e1) {
-                                               LOGGER.error("Creating a condition object failed", e1);
-                                       }
-                                       
-                                       updateCondition();
-                               }
+                               cond.conditions.add(createPropertyCondition("", null, null));
+                               updateDialog();
                        }
                });
                
-               removeConditionButton.addSelectionListener(new SelectionAdapter() {
+               return validate -> {
+                       for (Updater updater : updates)
+                               updater.update(validate);
+               };
+       }
+
+       private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
+               GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
+               GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
+               
+               Condition c = parentCondition.conditions.get(i);
+               
+               Composite row = new Composite(parent, SWT.NONE);
+               conditionLayout.applyTo(row);
+               swtDefaults.applyTo(row);
+               
+               Consumer<Condition> update = cd -> {
+                       if (cd != null)
+                               parentCondition.conditions.set(i, cd);
+                       else
+                               parentCondition.conditions.remove(i);
+               };
+               
+               Updater updater = createConditionPanelFor(row, c, update);
+               
+               Button removeButton = new Button(parent, SWT.PUSH);
+               swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
+               removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
+               
+               removeButton.addSelectionListener(new SelectionAdapter() {
                        @Override
                        public void widgetSelected(SelectionEvent e) {
-                               condition = null;
-                               updateCondition();
+                               parentCondition.conditions.remove(i);
+                               updateDialog();
                        }
                });
                
-               return content;
-       }
-       
-       private void updateCondition() {
-               if (condition != null) {
-                       removeConditionButton.setEnabled(true);
-                       try {
-                               conditionLabel.setText(ElementSelector.getExpression(Simantics.getSession(), condition.resource));
-                       } catch (DatabaseException e) {
-                               LOGGER.error("Error getting expression string for " + condition.resource);
-                       }
-               }
-               else {
-                       conditionLabel.setText("No condition");
-                       removeConditionButton.setEnabled(false);
-               }
-               
-               content.layout();
+               return updater;
        }
 
-       private boolean isDiagramFieldVisible() {
-               return generatorIndex == 1;
-       }
+       private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
+               ROW_LAYOUT.applyTo(conditionComp);
 
-       private boolean isSelectorCompositeVisible() {
-               return selectorIndex != 0;
+               // Create combo-box
+               Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
+               RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(routeCombo);
+               routeCombo.setItems(routeNames);
+               
+               // Set current selection
+               int index = Arrays.indexOf(routeResources, condition.routeResource);
+               if (index >= 0)
+                       routeCombo.select(index);
+
+               // Register update
+               return validate -> {
+                       int i = routeCombo.getSelectionIndex();
+                       if (validate && i < 0) {
+                               routeCombo.forceFocus();
+                               throw new RuntimeException("Must select a route");
+                       }
+                               
+                       condition.routeResource = i >= 0 ? routeResources[i] : null;
+               };
        }
 
-       class ConditionDialog extends Dialog {
-               // Resource of the edited condition
-               private Resource existingResource;
+       private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
+               ROW_LAYOUT.applyTo(conditionComp);
                
-               // Inverse condition button
-               private boolean isInverse;
-               private Button inverseField;
-
-               // Condition type
-               private int typeIndex;
-               private Combo typeField;
+               // Create combo-box
+               Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
+               RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(regionCombo);
+               regionCombo.setItems(regionNames);
                
-               // Type-specific control panels under a stack layout
-               private Composite stackPanel;
-               private StackLayout stack;
+               // Set current selection
+               int index = Arrays.indexOf(regionResources, condition.regionResource);
+               if (index >= 0)
+                       regionCombo.select(index);
                
-               private Composite propertyPanel;
-               private Composite regionPanel;
-               private Composite routePanel;
-               private Composite aggregatePanel;
+               // Register update
+               return validate -> {
+                       int i = regionCombo.getSelectionIndex();
+                       if (validate && i < 0) {
+                               regionCombo.forceFocus();
+                               throw new ValidationException("Please select a region");
+                       }
+                       
+                       condition.regionResource = i >= 0 ? regionResources[i] : null;
+               };
+       }
+
+       private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
+               ROW_LAYOUT.applyTo(conditionComp);
                
-               // Property condition
-               private Double lowerLimit;
-               private Double upperLimit;
-               private String propertyName;
-       
-               private Text propertyNameField;
-               private Text lowerLimitField;
-               private Text upperLimitField;
+               Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
+               RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(lowerLimitText);
+               lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
                
-               // Region condition
-               private Resource region;
-               private Combo regionField;
+               new Label(conditionComp, SWT.NONE).setText("\u2264");
                
-               // Route condition
-               private Resource route;
-               private Combo routeField;
+               Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
+               RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyNameText);
+               propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
+               int index = propertyNames.indexOf(condition.propertyName);
+               if (index >= 0)
+                       propertyNameText.select(index);
+               else
+                       propertyNameText.setText(condition.propertyName);
                
-               // Aggregate condition
-               private List<Condition> subConditions;
-               private boolean isConjunction;
+               new Label(conditionComp, SWT.NONE).setText("\u2264");
                
-               private org.eclipse.swt.widgets.List subConditionField;
-               private Combo operatorField;
+               Text upperLimitText = new Text(conditionComp, SWT.BORDER);
+               RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(upperLimitText);
+               upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
                
-               public ConditionDialog(Shell shell, Condition condition) {
-                       super(shell);
-                       
-                       typeIndex = 0;
-                       isInverse = false;
-                       propertyName = "";
-                       upperLimit = null;
-                       lowerLimit = null;
-                       subConditions = new ArrayList<>();
-                       
-                       existingResource = condition != null ? condition.resource : null;
-
-                       if (condition != null) {
-                               if (condition instanceof PropertyCondition) {
-                                       typeIndex = 0;
-                                       PropertyCondition propertyCondition = (PropertyCondition)condition;
-                                       propertyName = propertyCondition.propertyName;
-                                       upperLimit = propertyCondition.upperLimit;
-                                       lowerLimit = propertyCondition.lowerLimit;
-                               }
-                               else if (condition instanceof RegionCondition) {
-                                       typeIndex = 1;
-                                       region = ((RegionCondition)condition).regionResource;
-                               }
-                               else if (condition instanceof RouteCondition) {
-                                       typeIndex = 2;
-                                       route = ((RouteCondition)condition).routeResource;
-                               }
-                               else if (condition instanceof AggregateCondition) {
-                                       typeIndex = 3;
-                                       subConditions = new ArrayList<>(((AggregateCondition)condition).conditions);
-                                       isConjunction = ((AggregateCondition)condition).type == Type.CONJUNCTION;
-                                       isInverse = ((AggregateCondition)condition).type == Type.NEGATION;
+               // Register update
+               return validate ->  {
+                       try {
+                               String text = lowerLimitText.getText();
+                               condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
+                       } catch (NumberFormatException e) {
+                               if (validate) {
+                                       lowerLimitText.selectAll();
+                                       lowerLimitText.forceFocus();
+                                       throw new ValidationException("Please enter a valid lower limit");
                                }
                        }
-               }
-               
-               @Override
-               protected Control createDialogArea(Composite parent) {
-                       getShell().setText("Edit selector condition");
-                       
-                       Composite content = (Composite)super.createDialogArea(parent);
-                       GridLayoutFactory.swtDefaults().numColumns(1).applyTo(content);
-                       
-                       GridDataFactory defaultWidth = GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT);
-                       
-                       // Is inverse
-                       inverseField = new Button(content, SWT.CHECK);
-                       inverseField.setText("Is inverse");
-                       inverseField.setSelection(isInverse);
-                       
-                       // Condition type
-                       typeField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
-                       typeField.setItems("Property value", "In region", "On route", "Combination");
-                       typeField.select(typeIndex);
-                       
-                       // Type-dependent stacked panels
-                       stackPanel = new Composite(content, SWT.NONE);
-                       stack = new StackLayout();
-                       stackPanel.setLayout(stack);
-                       
-                       // Property condition panel
-                       propertyPanel = new Composite(stackPanel, SWT.NONE);
-                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(propertyPanel);
-                       
-                       new Label(propertyPanel, SWT.NONE).setText("Property name");
-                       propertyNameField = new Text(propertyPanel, SWT.BORDER);
-                       propertyNameField.setText(propertyName);
-                       defaultWidth.applyTo(propertyNameField);
-                       
-                       new Label(propertyPanel, SWT.NONE).setText("Lower limit");
-                       lowerLimitField = new Text(propertyPanel, SWT.BORDER);
-                       defaultWidth.applyTo(lowerLimitField);
-                       if (lowerLimit != null) lowerLimitField.setText(lowerLimit.toString());
-                       
-                       new Label(propertyPanel, SWT.NONE).setText("Upper limit");
-                       upperLimitField = new Text(propertyPanel, SWT.BORDER);
-                       defaultWidth.applyTo(upperLimitField);
-                       if (upperLimit != null) upperLimitField.setText(upperLimit.toString());
-                       
-                       // Region condition panel
-                       regionPanel = new Composite(stackPanel, SWT.NONE);
-                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(regionPanel);
-                       
-                       new Label(regionPanel, SWT.NONE).setText("Region");
-                       regionField = new Combo(regionPanel, SWT.BORDER | SWT.READ_ONLY);
-                       regionField.setItems(regionNames);
-                       defaultWidth.applyTo(regionField);
-                       
-                       if (region != null) {
-                               int regionIndex = Arrays.indexOf(regionResources, region);
-                               regionField.select(regionIndex);
-                       }
-                       else {
-                               regionField.select(0);
-                       }
-                       
-                       // Route condition panel
-                       routePanel = new Composite(stackPanel, SWT.NONE);
-                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(routePanel);
-                       
-                       new Label(routePanel, SWT.NONE).setText("Route");
-                       routeField = new Combo(routePanel, SWT.BORDER | SWT.READ_ONLY);
-                       routeField.setItems(routeNames);
-                       defaultWidth.applyTo(routeField);
-                       
-                       if (route != null) {
-                               int routeIndex = Arrays.indexOf(routeResources, route);
-                               routeField.select(routeIndex);
-                       }
-                       else {
-                               routeField.select(0);
-                       }
-                       
-                       // Aggregate condition panel
-                       aggregatePanel = new Composite(stackPanel, SWT.NONE);
-                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(aggregatePanel);
-                       
-                       new Label(aggregatePanel, SWT.NONE).setText("Operator");
-                       operatorField = new Combo(aggregatePanel, SWT.READ_ONLY);
-                       operatorField.setItems("And", "Or");
-                       operatorField.select(isConjunction ? 0 : 1);
-                       
-                       new Label(aggregatePanel, SWT.NONE).setText("Sub-conditions");
-                       Composite buttons = new Composite(aggregatePanel, SWT.NONE);
-                       RowLayoutFactory.swtDefaults().justify(true).fill(true).extendedMargins(0, 0, 0, 0).type(SWT.HORIZONTAL).applyTo(buttons);
                        
-                       Button addButton = new Button(buttons, SWT.PUSH);
-                       addButton.setText("Add");
-                       Button removeButton = new Button(buttons, SWT.PUSH);
-                       removeButton.setText("Remove");
-                       Button editButton = new Button(buttons, SWT.PUSH);
-                       editButton.setText("Edit");
-
-                       new Label(aggregatePanel, SWT.NONE);
-                       subConditionField = new org.eclipse.swt.widgets.List(aggregatePanel, SWT.BORDER);
-                       GridDataFactory.swtDefaults().hint(200, 150).applyTo(subConditionField);
-                       if (subConditions != null) {
-                               Session session = Simantics.getSession();
-                               List<String> items = new ArrayList<>();
-                               for (Condition c : subConditions) {
-                                       try {
-                                               items.add(ElementSelector.getExpression(session, c.resource));
-                                       } catch (DatabaseException e1) {
-                                               LOGGER.error("Condition expression read failed", e1);
-                                               items.add("<Unknown expression>");
-                                       }
+                       try {
+                               String text = upperLimitText.getText();
+                               condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
+                       } catch (NumberFormatException e) {
+                               if (validate) {
+                                       upperLimitText.selectAll();
+                                       upperLimitText.forceFocus();
+                                       throw new ValidationException("Please enter a valid upper limit");
                                }
-                               
-                               subConditionField.setItems(items.toArray(new String[items.size()]));
                        }
                        
-                       addButton.addSelectionListener(new SelectionAdapter() {
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       ConditionDialog conditionDialog = new ConditionDialog(getShell(), null);
-                                       if (conditionDialog.open() == Window.OK) {
-                                               Condition condition;
-                                               try {
-                                                       condition = conditionDialog.createCondition();
-                                                       subConditions.add(condition);
-                                                       
-                                                       try {
-                                                               subConditionField.add(ElementSelector.getExpression(Simantics.getSession(), condition.resource));
-                                                       } catch (DatabaseException e1) {
-                                                               LOGGER.error("Condition expression read failed", e1);
-                                                               subConditionField.add("<Unknown expression>");
-                                                       }
-                                               } catch (DatabaseException e2) {
-                                                       LOGGER.error("Create condition failed", e2);
-                                               }
-                                       }
-                               }
-                       });
+                       int ind = propertyNameText.getSelectionIndex();
+                       String name;
+                       if (ind >= 0)
+                               name = propertyNames.get(ind);
+                       else
+                               name = propertyNameText.getText();
                        
-                       removeButton.addSelectionListener(new SelectionAdapter() {
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       int index = subConditionField.getSelectionIndex();
-                                       if (index >= 0) {
-                                               subConditionField.deselectAll();
-                                               subConditionField.remove(index);
-                                               subConditions.remove(index);
-                                               
-                                               if (index < subConditions.size())
-                                                       subConditionField.setSelection(index);
-                                       }
-                                       
-                                       boolean selected = subConditionField.getSelectionIndex() >= 0;
-                                       removeButton.setEnabled(selected);
-                                       editButton.setEnabled(selected);
-                               }
-                       });
-                       
-                       editButton.addSelectionListener(new SelectionAdapter() {
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       int index = subConditionField.getSelectionIndex();
-                                       if (index >= 0) {
-                                               Condition condition = subConditions.get(index);
-                                               ConditionDialog conditionDialog = new ConditionDialog(getShell(), condition);
-                                               if (conditionDialog.open() == Window.OK) {
-                                                       try {
-                                                               condition = conditionDialog.createCondition();
-                                                               subConditions.set(index, condition);
-                                                               
-                                                               try {
-                                                                       subConditionField.setItem(index, ElementSelector.getExpression(Simantics.getSession(), condition.resource));
-                                                               } catch (DatabaseException e1) {
-                                                                       LOGGER.error("Condition expression read failed", e1);
-                                                                       subConditionField.setItem(index, "<Unknown expression>");
-                                                               }
-                                                       } catch (DatabaseException e2) {
-                                                               LOGGER.error("Create condition failed", e2);
-                                                       }
-                                               }
-                                       }
-                               }
-                       });
-                       
-                       subConditionField.addSelectionListener(new SelectionAdapter() {
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       boolean selected = subConditionField.getSelectionIndex() >= 0;
-                                       removeButton.setEnabled(selected);
-                                       editButton.setEnabled(selected);
-                               }
-                       });
-
-                       // Stack layout update
-                       typeField.addSelectionListener(new SelectionAdapter() {
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       updateStack();
-                               }
-                       });
-                       
-                       updateStack();
-                       
-                       return content;
-               }
-               
-               @Override
-               protected void okPressed() {
-                       isInverse = inverseField.getSelection();
-                       
-                       switch (typeIndex) {
-                       case 0: // Property condition
-                               propertyName = propertyNameField.getText();
-                               try {
-                                       String lowerLimitText = lowerLimitField.getText();
-                                       lowerLimit = lowerLimitText.equals("") ? null : Double.valueOf(lowerLimitText);
-                                       String upperLimitText = upperLimitField.getText();
-                                       upperLimit = upperLimitText.equals("") ? null : Double.valueOf(upperLimitText);
-                               }
-                               catch (NumberFormatException e) {
-                                       ErrorDialog.openError(getShell(), "Error", "Invalid numeric value: " + e.getMessage(), new Status(OK, "org.simantics.district.selection.ui", e.getMessage()));
-                                       return;
-                               }
-                               break;
-                       case 1: { // Region condition
-                               int selectionIndex = regionField.getSelectionIndex();
-                               if (selectionIndex < 0) {
-                                       ErrorDialog.openError(getShell(), "Error", "Please select a region", new Status(OK, "org.simantics.district.selection.ui", "No region selection"));
-                                       return;
-                               }
-                               region = regionResources[selectionIndex];
-                               break;
-                       }
-                       case 2: // Route condition
-                               route = routeResources[routeField.getSelectionIndex()];
-                               break;
-                       case 3: // Aggregate condition
-                               isConjunction = operatorField.getSelectionIndex() == 0;
-                               break;
+                       if (validate && name.isEmpty()) {
+                               propertyNameText.forceFocus();
+                               throw new ValidationException("Please select a property");
+                       } else {
+                               condition.propertyName = name;
                        }
-                       
-                       super.okPressed();
-               }
+               };
+       }
+
+       private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
+               return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
+       }
        
-               protected Condition createCondition() throws DatabaseException {
-                       if (isInverse && !(typeIndex == 3 && !isConjunction)) {
-                               Resource resource0 = createCondition0();
-                               
-                               // Create a negation
-                               Resource resource = Simantics.getSession().syncRequest(new WriteResult<Resource>() {
-                                       @Override
-                                       public Resource perform(WriteGraph graph) throws DatabaseException {
-                                               ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
-                                               Layer0 L0 = Layer0.getInstance(graph);
-                                               
-                                               Resource r = graph.newResource();
-                                               graph.claim(r, L0.InstanceOf, ES.Negation);
-                                               graph.claim(r, ES.HasSubcondition, resource0);
-                                               return r;
-                                       }
-                               });
-                               
-                               return ElementSelector.getCondition(Simantics.getSession(), resource);
-                       }
-                       else {
-                               return ElementSelector.getCondition(Simantics.getSession(), createCondition0());
-                       }
-               }
+       private static Condition createRegionCondition(Resource regionResource) {
+               return new RegionCondition(null, regionResource, null);
+       }
        
-               private Resource createCondition0() throws DatabaseException {
-                       switch (typeIndex) {
-                       case 0: return createPropertyCondition();
-                       case 1: return createRegionCondition();
-                       case 2: return createRouteCondition();
-                       case 3: return createAggregateCondition();
-                       default: throw new IllegalStateException("Invalid condition type code " + typeIndex);
-                       }
-               }
+       private static Condition createRouteCondition(Resource route) {
+               return new RouteCondition(null, route, null);
+       }
        
-               private Resource createPropertyCondition() throws DatabaseException {
-                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
-                               @Override
-                               public Resource perform(WriteGraph graph) throws DatabaseException {
-                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
-                                       Layer0 L0 = Layer0.getInstance(graph);
-                                       
-                                       Resource r = graph.newResource();
-                                       graph.claim(r, L0.InstanceOf, ES.PropertyCondition);
-                                       graph.claimLiteral(r, ES.PropertyCondition_HasPropertyName, propertyName);
-                                       if (lowerLimit != null)
-                                               graph.claimLiteral(r, ES.PropertyCondition_HasLowerLimit, L0.Double, lowerLimit);
-                                       if (upperLimit != null)
-                                               graph.claimLiteral(r, ES.PropertyCondition_HasUpperLimit, L0.Double, upperLimit);
-                                       
-                                       return r;
-                               }
-                       });
-               }
+       private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
+               Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
+               AggregateCondition condition = new AggregateCondition(null, type, subConditions);
+               condition.isInverse = isInverse;
+               return condition;
+       }
+       
+       static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
+               DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+               Resource project = Simantics.getProjectResource();
+               Resource model = ActiveModels.getPossibleActiveModel(graph, project);
                
-               private Resource createAggregateCondition() throws DatabaseException {
-                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
-                               @Override
-                               public Resource perform(WriteGraph graph) throws DatabaseException {
-                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
-                                       Layer0 L0 = Layer0.getInstance(graph);
-                                       
-                                       Resource r;
-                                       if (existingResource != null) {
-                                               // Reuse existing resource
-                                               r = existingResource;
-                                               // Clear any previous statements
-                                               graph.deny(existingResource);
-                                       }
-                                       else {
-                                               r = graph.newResource();
-                                       }
-                                       
-                                       Resource type;
-                                       type = isConjunction ? ES.Conjunction : isInverse ? ES.Negation : ES.Disjunction;
-                                       
-                                       graph.claim(r, L0.InstanceOf, type);
-                                       for (Condition c : subConditions) {
-                                               graph.claim(r, ES.HasSubcondition, c.resource);
-                                       }
-                                       
-                                       return r;
-                               }
-                       });
-               }
+               return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
+       }
        
-               private Resource createRouteCondition() throws DatabaseException {
-                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+       void updatePropertyList() {
+               propertyNames = new ArrayList<>();
+               propertyLabels = new ArrayList<>();
+               
+               Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
+               Set<Pair<String, String>> properties = new HashSet<>();
+               
+               try {
+                       Simantics.getSession().syncRequest(new ReadRequest() {
                                @Override
-                               public Resource perform(WriteGraph graph) throws DatabaseException {
-                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                               public void run(ReadGraph graph) throws DatabaseException {
                                        Layer0 L0 = Layer0.getInstance(graph);
                                        
-                                       Resource r = graph.newResource();
-                                       graph.claim(r, L0.InstanceOf, ES.RouteCondition);
-                                       graph.claim(r, ES.RouteCondition_HasRoute, route);
-                                       return r;
+                                       for (Resource type : types) {
+                                               if (type == null)
+                                                       continue;
+                                               
+                                               Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
+                                               if (ct == null)
+                                                       continue;
+                                               
+                                               if (graph.isInstanceOf(ct, L0.String)) {
+                                                       Resource indexRoot = graph.syncRequest(new IndexRoot(type));
+                                                       String name = graph.getValue(ct);
+                                                       ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
+                                                       if (ct == null)
+                                                               continue;
+                                               }
+                                               
+                                               for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
+                                                       if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
+                                                               continue;
+                                                       
+                                                       // Filter only numeric properties
+                                                       PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
+                                                       if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
+                                                               continue;
+                                                       
+                                                       String name = graph.getRelatedValue2(prop, L0.HasName);
+                                                       String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
+                                                       if (label == null) label = name;
+                                                       
+                                                       properties.add(Pair.make(label, name));
+                                               }
+                                       }
                                }
                        });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read district component properties", e);
                }
+               
+               propertyNames.clear();
+               propertyLabels.clear();
+               properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
+                       propertyLabels.add(p.first);
+                       propertyNames.add(p.second);
+               });
+       }
        
-               private Resource createRegionCondition() throws DatabaseException {
-                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
-                               @Override
-                               public Resource perform(WriteGraph graph) throws DatabaseException {
-                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
-                                       Layer0 L0 = Layer0.getInstance(graph);
-                                       
-                                       Resource r = graph.newResource();
-                                       graph.claim(r, L0.InstanceOf, ES.RegionCondition);
-                                       graph.claim(r, ES.RegionCondition_HasRegion, region);
-                                       return r;
-                               }
-                       });
-               }
-
-               private void updateStack() {
-                       typeIndex = typeField.getSelectionIndex();
-                       switch (typeIndex) {
-                       case 0: stack.topControl = propertyPanel; break;
-                       case 1: stack.topControl = regionPanel; break;
-                       case 2: stack.topControl = routePanel; break;
-                       case 3: stack.topControl = aggregatePanel; break;
-                       }
-                       
-                       stackPanel.layout();
+       static boolean isNumericValueType(String requiredValueType) {
+               switch (requiredValueType) {
+               case "Integer":
+               case "Long":
+               case "Double":
+               case "Float":
+                       return true;
+               default:
+                       return false;
                }
        }
 }
index 5a0767f8020c38708847f6aabe2fa0425b31c473..dfc53328a959885f0ec326e60e1c83be16f9a20e 100644 (file)
@@ -20,7 +20,6 @@ import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
 import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
 import org.eclipse.e4.ui.services.EMenuService;
 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.e4.ui.workbench.modeling.ISelectionListener;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.swt.SWT;
index 55e019585124ebb8bfd587373a5dd297050918ba..220714032395b21f609ddeb9a3ba36706adc78ee 100644 (file)
@@ -42,6 +42,8 @@ ES.PropertySelector <T ES.Selector
 ES.Selector.NLowest <T ES.PropertySelector
 ES.Selector.NHighest <T ES.PropertySelector
 
+ES.Selector.HasMapping <R L0.IsRelatedTo : L0.FunctionalRelation
+
 ES.Condition
     >-- ES.Condition.IsInverse --> ES.Condition <R L0.HasProperty : L0.Tag
 
index 7cd7a7fac4292b337c6bd4b2d0d99a4f6b44f579..913076ddcc694fa0531ba5fa3e56faf50eea84f9 100644 (file)
@@ -56,6 +56,7 @@ public class ElementSelectionResource {
     public final Resource Selection_HasSelector_Inverse;
     public final Resource Selector;
     public final Resource Selector_All;
+    public final Resource Selector_HasMapping;
     public final Resource Selector_NHighest;
     public final Resource Selector_NLowest;
         
@@ -106,6 +107,7 @@ public class ElementSelectionResource {
         public static final String Selection_HasSelector_Inverse = "http://www.simantics.org/ElementSelection-1.0/Selection/HasSelector/Inverse";
         public static final String Selector = "http://www.simantics.org/ElementSelection-1.0/Selector";
         public static final String Selector_All = "http://www.simantics.org/ElementSelection-1.0/Selector/All";
+        public static final String Selector_HasMapping = "http://www.simantics.org/ElementSelection-1.0/Selector/HasMapping";
         public static final String Selector_NHighest = "http://www.simantics.org/ElementSelection-1.0/Selector/NHighest";
         public static final String Selector_NLowest = "http://www.simantics.org/ElementSelection-1.0/Selector/NLowest";
     }
@@ -166,6 +168,7 @@ public class ElementSelectionResource {
         Selection_HasSelector_Inverse = getResourceOrNull(graph, URIs.Selection_HasSelector_Inverse);
         Selector = getResourceOrNull(graph, URIs.Selector);
         Selector_All = getResourceOrNull(graph, URIs.Selector_All);
+        Selector_HasMapping = getResourceOrNull(graph, URIs.Selector_HasMapping);
         Selector_NHighest = getResourceOrNull(graph, URIs.Selector_NHighest);
         Selector_NLowest = getResourceOrNull(graph, URIs.Selector_NLowest);
     }
index f0cbf4b1af9d02bfe0bee56b9eea098561e499be..9015b7117605674dd56bcaa9385e3116ccaedf43 100644 (file)
@@ -13,12 +13,20 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.RequestProcessor;
 import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.ResourceRead;
 import org.simantics.db.common.utils.ListUtils;
+import org.simantics.db.exception.AssumptionException;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.DoesNotContainValueException;
+import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
+import org.simantics.db.exception.NoSingleResultException;
+import org.simantics.db.exception.ServiceException;
+import org.simantics.db.exception.ValidationException;
 import org.simantics.db.layer0.QueryIndexUtils;
 import org.simantics.db.layer0.request.ActiveModels;
 import org.simantics.db.layer0.request.ActiveRuns;
@@ -31,7 +39,6 @@ import org.simantics.db.request.Read;
 import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.region.ontology.DiagramRegionsResource;
-import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
 import org.simantics.layer0.Layer0;
 import org.simantics.modeling.ModelingResources;
 import org.simantics.structural.stubs.StructuralResource2;
@@ -163,20 +170,26 @@ public class ElementSelector {
                        return Simantics.getSession().syncRequest(new Read<Map<Resource, String>>() {
                                @Override
                                public Map<Resource, String> perform(ReadGraph graph) throws DatabaseException {
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       StructuralResource2 STR = StructuralResource2.getInstance(graph);
+                                       DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+                                       ModelingResources MOD = ModelingResources.getInstance(graph);
+                                       
                                        Map<Resource, String> result = new HashMap<>();
                                        Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
-                                       List<Resource> composites = QueryIndexUtils.searchByType(graph, model, StructuralResource2.getInstance(graph).Composite);
+                                       List<Resource> composites = QueryIndexUtils.searchByType(graph, model, STR.Composite);
                                        for (Resource r : composites) {
                                                // Get diagram
-                                               Resource diagram = graph.getPossibleObject(r, ModelingResources.getInstance(graph).CompositeToDiagram);
-                                               if (diagram == null) continue;
+                                               Resource diagram = graph.getPossibleObject(r, MOD.CompositeToDiagram);
+                                               if (diagram == null || !graph.isInstanceOf(diagram, DN.Diagram))
+                                                       continue;
                                                
                                                // Filter out user component diagrams
-                                               Resource parent = graph.getPossibleObject(r, Layer0.getInstance(graph).PartOf);
-                                               if (parent == null || graph.isInheritedFrom(parent, StructuralResource2.getInstance(graph).Component))
+                                               Resource parent = graph.getPossibleObject(r, L0.PartOf);
+                                               if (parent == null || graph.isInheritedFrom(parent, STR.Component))
                                                        continue;
                                                
-                                               result.put(r, graph.getRelatedValue(r, Layer0.getInstance(graph).HasName));
+                                               result.put(r, graph.getRelatedValue(r, L0.HasName));
                                        }
                                        
                                        return result;
@@ -246,18 +259,14 @@ public class ElementSelector {
                if (!graph.isInstanceOf(resource, ES.Generator))
                        throw new IllegalArgumentException("Resource " + resource + " is not a valid generator");
                
-               if (graph.isInstanceOf(resource, ES.Generator_Model)) {
+               if (graph.isInstanceOf(resource, ES.Generator_Model))
                        return new ModelGenerator();
-               }
-               else if (graph.isInstanceOf(resource, ES.Generator_Diagram)) {
+               else if (graph.isInstanceOf(resource, ES.Generator_Diagram))
                        return new DiagramGenerator(graph.getSingleObject(resource, ES.Generator_HasDiagram));
-               }
-               else if (graph.isInstanceOf(resource, ES.Generator_Explicit)) {
+               else if (graph.isInstanceOf(resource, ES.Generator_Explicit))
                        return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement));
-               }
-               else {
+               else
                        throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource)));
-               }
        }
 
        private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException {
@@ -265,18 +274,16 @@ public class ElementSelector {
                        throw new IllegalArgumentException("Resource " + resource + " is not a valid selector");
                
                Selector s;
-               if (graph.isInstanceOf(resource, ES.Selector_All)) {
+               if (graph.isInstanceOf(resource, ES.Selector_All))
                        s = new All();
-               }
-               else if (graph.isInstanceOf(resource, ES.PropertySelector)) {
-                       String propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName);
-                       Integer resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount);
-                       boolean isSmallest = graph.isInstanceOf(resource, ES.Selector_NLowest);
-                       s = new PropertySelector(isSmallest, propertyName, resultCount);
-               }
-               else {
+               else if (graph.isInstanceOf(resource, ES.PropertySelector))
+                       s = new PropertySelector(graph, resource);
+               else
                        throw new IllegalArgumentException("Unknown selector type " + graph.getURI(graph.getSingleType(resource)));
-               }
+               
+               Resource mapping = graph.getPossibleObject(resource, ES.Selector_HasMapping);
+               s.componentType = mapping;
+               
                return s;
        }
 
@@ -288,145 +295,179 @@ public class ElementSelector {
                        throw new IllegalArgumentException("Resource " + resource + " is not a valid condition");
                
                Condition cond;
-               if (graph.isInstanceOf(resource, ES.PropertyCondition)) {
-                       String propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName);
-                       Double lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit);
-                       Double upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit);
-                       cond = new PropertyCondition(resource, propertyName, lowerLimit, upperLimit);
+               if (graph.isInstanceOf(resource, ES.PropertyCondition))
+                       cond = new PropertyCondition(graph, resource);
+               else if (graph.isInstanceOf(resource, ES.RegionCondition))
+                       cond = new RegionCondition(graph, resource);
+               else if (graph.isInstanceOf(resource, ES.RouteCondition))
+                       cond = new RouteCondition(graph, resource);
+               else if (graph.isInstanceOf(resource, ES.AggregateCondition))
+                       cond = new AggregateCondition(graph, resource);
+               else
+                       throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
+               
+               return cond;
+       }
+
+       private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
+               if (graph.isInstanceOf(r, ES.Selection))
+                       return buildSelectionExpression(graph, r);
+               else if (graph.isInstanceOf(r, ES.Condition))
+                       return buildConditionExpression(graph, r);
+               else if (graph.isInstanceOf(r, ES.Selector))
+                       return buildSelectorExpression(graph, r);
+               else if (graph.isInstanceOf(r, ES.Generator))
+                       return buildGeneratorExpression(graph, r);
+               else
+                       throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
+       }
+
+       private static String buildSelectionExpression(ReadGraph graph, Resource r) throws DatabaseException,
+                       NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException {
+               String exp = "select " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
+                            " from " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
+               
+               Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
+               return cond != null ? exp + " where {" + getExpression(graph, cond) + "}" : exp;
+       }
+
+       private static String buildGeneratorExpression(ReadGraph graph, Resource r)
+                       throws ServiceException, NoSingleResultException, DoesNotContainValueException,
+                       ManyObjectsForFunctionalRelationException, DatabaseException, AssumptionException, ValidationException {
+               if (graph.isInstanceOf(r, ES.Generator_Model)) {
+                       return "model";
                }
-               else if (graph.isInstanceOf(resource, ES.RegionCondition)) {
-                       DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
-                       Resource regionResource = graph.getSingleObject(resource, ES.RegionCondition_HasRegion);
-                       double[] region = graph.getRelatedValue(regionResource, DR.Region_area);
-                       cond = new RegionCondition(resource, regionResource, region);
+               else if (graph.isInstanceOf(r, ES.Generator_Diagram)) {
+                       return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\"";
                }
-               else if (graph.isInstanceOf(resource, ES.RouteCondition)) {
-                       Resource routeResource = graph.getSingleObject(resource, ES.RouteCondition_HasRoute);
-                       Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, routeResource));
-                       cond = new RouteCondition(resource, routeResource, routePoints);
+               else if (graph.isInstanceOf(r, ES.Generator_Explicit)) {
+                       return "<list of " + graph.getObjects(r, ES.Generator_HasSelectedElement).size() + " elements>";
                }
-               else if (graph.isInstanceOf(resource, ES.AggregateCondition)) {
-                       Collection<Resource> conditionResources = graph.getObjects(resource, ES.HasSubcondition);
-                       List<Condition> conditions = new ArrayList<>(conditionResources.size());
-                       for (Resource c : conditionResources) {
-                               conditions.add(buildCondition(graph, c));
-                       }
-                       Type type;
-                       if (graph.isInstanceOf(resource, ES.Conjunction))
-                               type = AggregateCondition.Type.CONJUNCTION;
-                       else if (graph.isInstanceOf(resource, ES.Negation))
-                               type = AggregateCondition.Type.NEGATION;
-                       else if (graph.isInstanceOf(resource, ES.Disjunction))
-                               type = AggregateCondition.Type.DISJUNCTION;
-                       else
-                               throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource)));
-                               
-                       cond = new AggregateCondition(resource, type, conditions);
+               else {
+                       throw new DatabaseException("Unsupported generator resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
+               }
+       }
+
+       private static String buildSelectorExpression(ReadGraph graph, Resource r)
+                       throws ServiceException, NoSingleResultException, DoesNotContainValueException, DatabaseException,
+                       AssumptionException, ValidationException, ManyObjectsForFunctionalRelationException {
+               String exp;
+               if (graph.isInstanceOf(r, ES.Selector_All)) {
+                       exp = "all";
+               }
+               else if (graph.isInstanceOf(r, ES.PropertySelector)) {
+                       Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount);
+                       exp = count.toString();
                }
                else {
-                       throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
+                       throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
                }
                
-               return cond;
+               Resource mapping = graph.getPossibleObject(r, ES.Selector_HasMapping);
+               if (mapping != null) {
+                       String name = graph.getRelatedValue2(mapping, L0.HasName);
+                       exp = exp + " " + name;
+               } else {
+                       exp = exp + " elements";
+               }
+               
+               if (graph.isInstanceOf(r, ES.PropertySelector)) {
+                       String op;
+                       if (graph.isInstanceOf(r, ES.Selector_NLowest))
+                               op = "lowest";
+                       else if (graph.isInstanceOf(r, ES.Selector_NHighest))
+                               op = "highest";
+                       else
+                               throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
+                       
+                       String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName);
+                       exp = exp + " with " + op + " " + name;
+               }
+               
+               return exp;
        }
 
-       private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
-               if (graph.isInstanceOf(r, ES.Selection)) {
-                       String exp = "select " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
-                                    " from " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
-                       
-                       Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
-                       return cond != null ? exp + " where {" + getExpression(graph, cond) + "}" : exp;
+       private static String buildConditionExpression(ReadGraph graph, Resource r) throws ServiceException,
+                       DatabaseException, NoSingleResultException, ManyObjectsForFunctionalRelationException,
+                       DoesNotContainValueException, AssumptionException, ValidationException {
+               String result;
+               boolean isInverse = graph.hasStatement(r, ES.Condition_IsInverse, r);
+               if (graph.isInstanceOf(r, ES.PropertyCondition)) {
+                       result = buildPropertyConditionExpression(graph, r);
                }
-               else if (graph.isInstanceOf(r, ES.Condition)) {
-                       if (graph.isInstanceOf(r, ES.PropertyCondition)) {
-                               return buildPropertyConditionExpression(graph, r);
-                       }
-                       else if (graph.isInstanceOf(r, ES.RegionCondition)) {
-                               Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion);
-                               String name = graph.getRelatedValue(region, L0.HasLabel);
-                               return "in region " + name;
-                       }
-                       else if (graph.isInstanceOf(r, ES.RouteCondition)) {
-                               Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute);
-                               String name = graph.getRelatedValue(route, L0.HasLabel);
-                               return "in route " + name;
-                       }
-                       else if (graph.isInstanceOf(r, ES.AggregateCondition)) {
-                               String op = graph.isInstanceOf(r, ES.Conjunction) ? " and " : " or ";
-                               List<String> exps = new ArrayList<>();
-                               Collection<Resource> objects = graph.getObjects(r, ES.HasSubcondition);
-                               for (Resource c : objects) {
-                                       String exp = getExpression(graph, c);
-                                       exps.add(objects.size() > 1 ? "{" + exp + "}" : exp);
-                               }
-                               String result = String.join(op, exps);
-                               if (graph.isInstanceOf(r, ES.Negation))
-                                       result = "not {" + result + "}";
-                               return result;
-                       }
-                       else {
-                               throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
-                       }
+               else if (graph.isInstanceOf(r, ES.RegionCondition)) {
+                       result = buildRegionConditionExpression(graph, r);
                }
-               else if (graph.isInstanceOf(r, ES.Selector)) {
-                       if (graph.isInstanceOf(r, ES.Selector_All)) {
-                               return "all";
-                       }
-                       else if (graph.isInstanceOf(r, ES.PropertySelector)) {
-                               String op;
-                               if (graph.isInstanceOf(r, ES.Selector_NLowest))
-                                       op = "bottom";
-                               else if (graph.isInstanceOf(r, ES.Selector_NHighest))
-                                       op = "top";
-                               else
-                                       throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
-                               
-                               String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName);
-                               Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount);
-                               return op + " " + count + " of " + name;
-                       }
-                       else {
-                               throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
-                       }
+               else if (graph.isInstanceOf(r, ES.RouteCondition)) {
+                       result = buildRouteConditionExpression(graph, r);
                }
-               else if (graph.isInstanceOf(r, ES.Generator)) {
-                       if (graph.isInstanceOf(r, ES.Generator_Model)) {
-                               return "model";
-                       }
-                       else if (graph.isInstanceOf(r, ES.Generator_Diagram)) {
-                               return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\"";
-                       }
-                       else if (graph.isInstanceOf(r, ES.Generator_Explicit)) {
-                               return "<list of " + graph.getObjects(r, ES.Generator_HasSelectedElement).size() + " elements>";
-                       }
-                       else {
-                               throw new DatabaseException("Unsupported generator resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
-                       }
+               else if (graph.isInstanceOf(r, ES.AggregateCondition)) {
+                       // This handles isInverse internally
+                       return buildAggregateConditionExpression(graph, r, isInverse);
                }
                else {
-                       throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
+                       throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
                }
+               
+               if (isInverse)
+                       result = "not {" + result + "}";
+               
+               return result;
+       }
+
+       private static String buildAggregateConditionExpression(ReadGraph graph, Resource r, boolean isInverse)
+                       throws ServiceException, DatabaseException {
+               String result;
+               String op = graph.isInstanceOf(r, ES.Conjunction) ? " and " : " or ";
+               List<String> exps = new ArrayList<>();
+               Collection<Resource> objects = graph.getObjects(r, ES.HasSubcondition);
+               for (Resource c : objects) {
+                       String exp = getExpression(graph, c);
+                       exps.add(objects.size() > 1 ? "{" + exp + "}" : exp);
+               }
+               result = String.join(op, exps);
+               if (graph.isInstanceOf(r, ES.Negation) ^ isInverse)
+                       result = "not {" + result + "}";
+               return result;
+       }
+
+       private static String buildRouteConditionExpression(ReadGraph graph, Resource r) throws NoSingleResultException,
+                       ManyObjectsForFunctionalRelationException, ServiceException, DoesNotContainValueException {
+               String result;
+               Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute);
+               String name = graph.getRelatedValue(route, L0.HasLabel);
+               result = "in route " + name;
+               return result;
+       }
+
+       private static String buildRegionConditionExpression(ReadGraph graph, Resource r) throws NoSingleResultException,
+                       ManyObjectsForFunctionalRelationException, ServiceException, DoesNotContainValueException {
+               String result;
+               Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion);
+               String name = graph.getRelatedValue(region, L0.HasLabel);
+               result = "in region " + name;
+               return result;
        }
 
        private static String buildPropertyConditionExpression(ReadGraph graph, Resource r) throws DatabaseException {
                String propertyName = graph.getRelatedValue(r, ES.PropertyCondition_HasPropertyName);
                Double lowerLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasLowerLimit);
                Double upperLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasUpperLimit);
-               if (lowerLimit == null && upperLimit == null) {
-                       return "has property " + propertyName;
-               }
-               else {
+               if (upperLimit == null) {
+                       if (lowerLimit == null) {
+                               return "has property " + propertyName;
+                       } else {
+                               return propertyName + " \u2265 " + lowerLimit;
+                       }
+               } else {
                        StringBuilder result = new StringBuilder();
                        if (lowerLimit != null) {
                                result.append(lowerLimit);
-                               result.append(" < ");
+                               result.append(" \u2264 ");
                        }
                        result.append(propertyName);
-                       if (upperLimit != null) {
-                               result.append(" < ");
-                               result.append(upperLimit);
-                       }
+                       result.append(" \u2264 ");
+                       result.append(upperLimit);
                        return result.toString();
                }
        }
@@ -538,7 +579,26 @@ public class ElementSelector {
        }
 
        public static abstract class Selector {
+               public Resource componentType = null;
+               
                abstract SelectionResult select(ReadGraph graph, Collection<Resource> elements);
+               
+               Collection<Resource> filterElements(ReadGraph graph, Collection<Resource> elements) {
+                       if (componentType == null)
+                               return elements;
+                       
+                       Collection<Resource> selected = new HashSet<>(elements.size());
+                       for (Resource r : elements) {
+                               try {
+                                       if (graph.hasStatement(r, DN.HasMapping, componentType))
+                                               selected.add(r);
+                               } catch (DatabaseException e) {
+                                       // Just leave it out of the result
+                               }
+                       }
+                       
+                       return selected;
+               }
        }
        
        public static class All extends Selector {
@@ -547,7 +607,8 @@ public class ElementSelector {
 
                @Override
                SelectionResult select(ReadGraph graph, Collection<Resource> elements) {
-                       return new SelectionResult(elements, 0, 0);
+                       Collection<Resource> selected = filterElements(graph, elements);
+                       return new SelectionResult(selected, 0, 0);
                }
        }
        
@@ -558,12 +619,20 @@ public class ElementSelector {
                        this.resultCount = resultCount;
                }
                
+               public PropertySelector(ReadGraph graph, Resource resource) throws DatabaseException {
+                       this.propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName);
+                       this.resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount);
+                       this.smallest = graph.isInstanceOf(resource, ES.Selector_NLowest);
+               }
+
                public boolean smallest;
                public String propertyName;
                public int resultCount;
                
                @Override
                SelectionResult select(ReadGraph graph, Collection<Resource> elements) {
+                       elements = filterElements(graph, elements);
+                       
                        // Select sorting direction
                        Comparator<Pair<Resource, Double>> comparator = smallest ?
                                        (p1, p2) -> Double.compare(p1.second, p2.second) :
@@ -611,15 +680,42 @@ public class ElementSelector {
        
        public static abstract class Condition {
                public Resource resource;
+               public boolean isInverse;
                
                Condition(Resource r) {
                        resource = r;
+                       isInverse = false;
                }
                
-               abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
+               Condition(ReadGraph graph, Resource r) throws DatabaseException {
+                       this(r);
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       isInverse = graph.hasStatement(r, ES.Condition_IsInverse, r);
+               }
+               
+               public abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
+               public Resource update(WriteGraph graph) throws DatabaseException {
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       
+                       assert(resource != null);
+                       if (isInverse)
+                               graph.claim(resource, ES.Condition_IsInverse, resource);
+                       else
+                               graph.deny(resource, ES.Condition_IsInverse, resource);
+                       return resource;
+               }
        }
        
        public static class PropertyCondition extends Condition {
+               public PropertyCondition(ReadGraph graph, Resource r) throws DatabaseException {
+                       super(graph, r);
+                       
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       this.propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName);
+                       this.lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit);
+                       this.upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit);
+               }
+               
                public PropertyCondition(Resource r, String propertyName, Double lowerLimit, Double upperLimit) {
                        super(r);
                        
@@ -633,27 +729,68 @@ public class ElementSelector {
                public Double upperLimit;
                
                @Override
-               boolean match(ReadGraph graph, Resource r) {
+               public boolean match(ReadGraph graph, Resource r) {
                        Double value = getPropertyValue(graph, r, propertyName);
-                       return value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit);
+                       boolean result = value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit);
+                       return result ^ isInverse;
+               }
+               
+               @Override
+               public Resource update(WriteGraph graph) throws DatabaseException {
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       
+                       if (resource == null) {
+                               resource = graph.newResource();
+                               graph.claim(resource, L0.InstanceOf, ES.PropertyCondition);
+                       }
+                       
+                       super.update(graph);
+                       
+                       graph.claimLiteral(resource, ES.PropertyCondition_HasPropertyName, propertyName);
+                       if (lowerLimit != null)
+                               graph.claimLiteral(resource, ES.PropertyCondition_HasLowerLimit, L0.Double, lowerLimit);
+                       else
+                               graph.deny(resource, ES.PropertyCondition_HasLowerLimit);
+                       
+                       if (upperLimit != null)
+                               graph.claimLiteral(resource, ES.PropertyCondition_HasUpperLimit, L0.Double, upperLimit);
+                       else
+                               graph.deny(resource, ES.PropertyCondition_HasUpperLimit);
+                       return resource;
                }
        }
        
        public static class RegionCondition extends Condition {
-               public RegionCondition(Resource r, Resource regionResoruce, double[] region) {
+               public RegionCondition(Resource r, Resource regionResource, double[] region) {
                        super(r);
                        this.region = region;
+                       this.path = createPathForRegion(region);
+                       this.regionResource = regionResource;
+               }
+
+               public RegionCondition(ReadGraph graph, Resource r) throws DatabaseException {
+                       super(graph, r);
                        
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
+                       this.regionResource = graph.getPossibleObject(resource, ES.RegionCondition_HasRegion);
+                       this.region = regionResource != null ? graph.getRelatedValue(regionResource, DR.Region_area) : null;
+                       this.path = createPathForRegion(region);
+               }
+
+               public static Path2D createPathForRegion(double[] region) {
                        Path2D path = new Path2D.Double();
-                       double startX = region[0];
-                       double startY = region[1];
-                       path.moveTo(startX, startY);
-                       for (int i = 2; i < region.length; i+=2)
-                               path.lineTo(region[i], region[i+1]);
-                       path.closePath();
+                       if (region != null) {
+                               double startX = region[0];
+                               double startY = region[1];
+                               path.moveTo(startX, startY);
+                               for (int i = 2; i < region.length; i+=2)
+                                       path.lineTo(region[i], region[i+1]);
+                               path.closePath();
+                       }
 
-                       this.path = path;
-                       this.regionResource = regionResoruce;
+                       return path;
                }
 
                public Resource regionResource;
@@ -661,12 +798,34 @@ public class ElementSelector {
                Path2D path;
 
                @Override
-               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+               public boolean match(ReadGraph graph, Resource r) throws DatabaseException {
                        double[] transform = graph.getRelatedValue(r, DIA.HasTransform);
                        double x = transform[4];
                        double y = transform[5];
-                       return path.contains(x, y);
-               } 
+                       return path.contains(x, y) ^ isInverse;
+               }
+               
+               @Override
+               public Resource update(WriteGraph graph) throws DatabaseException {
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
+                       
+                       if (resource == null) {
+                               resource = graph.newResource();
+                               graph.claim(resource, L0.InstanceOf, ES.RegionCondition);
+                       }
+                       
+                       super.update(graph);
+                       
+                       graph.claim(resource, ES.RegionCondition_HasRegion, regionResource);
+                       
+                       // Re-read region data to match DB
+                       this.region = regionResource != null ? graph.getRelatedValue(regionResource, DR.Region_area, Bindings.DOUBLE_ARRAY) : null;
+                       this.path = createPathForRegion(region);
+                       
+                       return resource;
+               }
        }
        
        public static class RouteCondition extends Condition {
@@ -676,12 +835,44 @@ public class ElementSelector {
                        this.routeResource = routeResource;
                }
 
+               public RouteCondition(ReadGraph graph, Resource r) throws DatabaseException {
+                       super(graph, r);
+                       this.routeResource = graph.getPossibleObject(resource, ES.RouteCondition_HasRoute);
+                       this.routePoints = getRoutePoints(graph, routeResource);
+               }
+
+               public static Set<Resource> getRoutePoints(ReadGraph graph, Resource routeResource) throws DatabaseException {
+                       return routeResource != null ?
+                                       new HashSet<>(ListUtils.toList(graph, routeResource)) :
+                                       Collections.emptySet();
+               }
+
                public Resource routeResource;
                Set<Resource> routePoints;
 
                @Override
-               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
-                       return routePoints.contains(r);
+               public boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+                       return routePoints.contains(r) ^ isInverse;
+               }
+               
+               @Override
+               public Resource update(WriteGraph graph) throws DatabaseException {
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       
+                       if (resource == null) {
+                               resource = graph.newResource();
+                               graph.claim(resource, L0.InstanceOf, ES.RouteCondition);
+                       }
+                       
+                       super.update(graph);
+                       
+                       if (routeResource != null)
+                               graph.claim(resource, ES.RouteCondition_HasRoute, routeResource);
+                       
+                       this.routePoints = getRoutePoints(graph, routeResource);
+                       
+                       return resource;
                }
        }
        
@@ -694,11 +885,32 @@ public class ElementSelector {
                        this.conditions = conditions;
                }
                
+               public AggregateCondition(ReadGraph graph, Resource r) throws DatabaseException {
+                       super(graph, r);
+                       Collection<Resource> conditionResources = graph.getObjects(resource, ES.HasSubcondition);
+                       conditions = new ArrayList<>(conditionResources.size());
+                       for (Resource c : conditionResources) {
+                               conditions.add(buildCondition(graph, c));
+                       }
+                       if (graph.isInstanceOf(resource, ES.Conjunction))
+                               this.type = AggregateCondition.Type.CONJUNCTION;
+                       else if (graph.isInstanceOf(resource, ES.Negation))
+                               this.type = AggregateCondition.Type.NEGATION;
+                       else if (graph.isInstanceOf(resource, ES.Disjunction))
+                               this.type = AggregateCondition.Type.DISJUNCTION;
+                       else
+                               throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource)));
+               }
+
                public Type type;
                public List<Condition> conditions;
                
                @Override
-               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+               public boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+                       return doMatch(graph, r) ^ isInverse;
+               }
+               
+               private boolean doMatch(ReadGraph graph, Resource r) throws DatabaseException {
                        switch (type) {
                        case DISJUNCTION:
                                for (Condition c : conditions)
@@ -717,6 +929,41 @@ public class ElementSelector {
                                throw new IllegalArgumentException("Unknown aggregate condition type " + type);
                        }
                }
+               
+               @Override
+               public Resource update(WriteGraph graph) throws DatabaseException {
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       
+                       if (resource == null) {
+                               resource = graph.newResource();
+                       } else {
+                               graph.deny(resource, L0.InstanceOf);
+                               graph.deny(resource, ES.HasSubcondition);
+                       }
+                       
+                       Resource type;
+                       switch (this.type) {
+                       case CONJUNCTION:
+                               type = ES.Conjunction; break;
+                       case DISJUNCTION:
+                               type = ES.Disjunction; break;
+                       case NEGATION:
+                               type = ES.Negation; break;
+                       default:
+                               throw new IllegalStateException("Unknown condition type " + this.type);
+                       }
+                       
+                       graph.claim(resource, L0.InstanceOf, type);
+                       
+                       super.update(graph);
+                       
+                       for (Condition c : conditions) {
+                               graph.claim(resource, ES.HasSubcondition, c.update(graph));
+                       }
+                       
+                       return resource;
+               }
        }
        
        public static class ElementSelectorQuery extends ResourceRead<ElementSelector> {