]> gerrit.simantics Code Review - simantics/district.git/commitdiff
Implementation of non-UI parts of network element queries 31/2631/5
authorReino Ruusu <reino.ruusu@semantum.fi>
Wed, 30 Jan 2019 13:34:26 +0000 (15:34 +0200)
committerReino Ruusu <reino.ruusu@semantum.fi>
Wed, 30 Jan 2019 13:46:52 +0000 (15:46 +0200)
gitlab #28

TODO:
* Management of mapped properties for map diagram elements
* UI for listing stored queries
* UI for defining/editing queries
* UI for executing queries

Change-Id: I5de84629ed75ce1ae48cd424dc872bb3456ec62b

14 files changed:
org.simantics.district.feature/feature.xml
org.simantics.district.network.ontology/graph/DistrictNetworkProfiles.pgraph
org.simantics.district.region.ontology/graph/DiagramRegions.pgraph
org.simantics.district.selection/.classpath [new file with mode: 0644]
org.simantics.district.selection/.project [new file with mode: 0644]
org.simantics.district.selection/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.district.selection/build.properties [new file with mode: 0644]
org.simantics.district.selection/graph.tg [new file with mode: 0644]
org.simantics.district.selection/graph/DiagramElementSelection.pgraph [new file with mode: 0644]
org.simantics.district.selection/pom.xml [new file with mode: 0644]
org.simantics.district.selection/scl/Simantics/District/Selection.scl [new file with mode: 0644]
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionResource.java [new file with mode: 0644]
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java [new file with mode: 0644]
pom.xml

index 47cc1761548b829b3fe88c61dbdeaa1f6e309aae..f08da0a1749036a23bc61435073de5fe74013d68 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.district.selection"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
index 1a468d12696cca2c3822eebe555ca0f8c2812d01..d3734e63c0103873bf17a0e7278b31c9082de093 100644 (file)
@@ -30,5 +30,5 @@ DN.VertexSymbolStyle : DIA.Style
 // The output of the function should be SVG
 DN.HasSymbolFunction <R L0.HasProperty
     <-- DIA.ProfileEntry
-    --> L0.ExternalValue
+    --> L0.Value
     ==> "Resource -> <ReadGraph> String"
index c7774dba38e8f8ccd5bba60257f882fd9698e6ff..db8b80fa18609490d882293ee02acef62e9eac9a 100644 (file)
@@ -8,6 +8,7 @@ DIARegions = <http://www.simantics.org/DistrictDiagramRegions-1.0> : L0.Ontology
 DIARegions.Region <T L0.Entity
 
 DIARegions.Region.area <R L0.HasProperty
+    ==> "Vector Double"
 
 DIARegions.hasRegion <R L0.IsWeaklyRelatedTo
     L0.InverseOf DIARegions.regionOf <R L0.IsWeaklyRelatedTo
diff --git a/org.simantics.district.selection/.classpath b/org.simantics.district.selection/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.simantics.district.selection/.project b/org.simantics.district.selection/.project
new file mode 100644 (file)
index 0000000..4401353
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.district.selection</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.simantics.graph.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.simantics.graph.nature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.simantics.district.selection/META-INF/MANIFEST.MF b/org.simantics.district.selection/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..ae19b9c
--- /dev/null
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Diagram Element Selection
+Bundle-SymbolicName: org.simantics.district.selection
+Bundle-Version: 1.0.0.qualifier
+Automatic-Module-Name: org.simantics.district.selection
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.simantics.district.route;bundle-version="1.0.0",
+ org.simantics.district.route.ontology;bundle-version="1.0.0",
+ org.simantics.district.region;bundle-version="1.0.0",
+ org.simantics.district.region.ontology;bundle-version="1.0.0",
+ org.simantics.db.layer0;bundle-version="1.1.0",
+ org.simantics.layer0;bundle-version="1.1.0",
+ org.simantics.diagram.ontology;bundle-version="2.2.0",
+ org.simantics.district.network.ontology;bundle-version="1.0.0",
+ org.slf4j.api;bundle-version="1.7.25",
+ org.junit,
+ org.simantics,
+ org.simantics.db.testing,
+ org.simantics.structural.ontology,
+ org.simantics.modeling.ontology
+Export-Package: org.simantics.district.selection
diff --git a/org.simantics.district.selection/build.properties b/org.simantics.district.selection/build.properties
new file mode 100644 (file)
index 0000000..07dd878
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               scl/,\
+               graph.tg
diff --git a/org.simantics.district.selection/graph.tg b/org.simantics.district.selection/graph.tg
new file mode 100644 (file)
index 0000000..d7f0261
Binary files /dev/null and b/org.simantics.district.selection/graph.tg differ
diff --git a/org.simantics.district.selection/graph/DiagramElementSelection.pgraph b/org.simantics.district.selection/graph/DiagramElementSelection.pgraph
new file mode 100644 (file)
index 0000000..5a51086
--- /dev/null
@@ -0,0 +1,72 @@
+L0 = <http://www.simantics.org/Layer0-1.1>
+DIA = <http://www.simantics.org/Diagram-2.2>
+DNR = <http://www.simantics.org/DistrictNetworkRoutes-1.0>
+REG = <http://www.simantics.org/DistrictDiagramRegions-1.0>
+
+ES = <http://www.simantics.org/ElementSelection-1.0> : L0.Ontology
+    @L0.new
+    L0.Ontology.global true
+    L0.HasResourceClass "org.simantics.district.selection.ElementSelectionResource"
+
+ES.Selection <T L0.Entity
+    >-- ES.Selection.HasGenerator --> ES.Generator <R L0.HasProperty : L0.FunctionalRelation
+    >-- ES.Selection.HasCondition --> ES.Condition <R L0.HasProperty : L0.FunctionalRelation
+    >-- ES.Selection.HasSelector --> ES.Selector <R L0.HasProperty : L0.FunctionalRelation
+    @L0.singleProperty ES.Selection.HasGenerator
+    @L0.singleProperty ES.Selection.HasCondition
+    @L0.singleProperty ES.Selection.HasSelector
+
+ES.Generator <T L0.Entity
+ES.Condition <T L0.Entity
+ES.Selector <T L0.Entity
+
+ES.Generator.Explicit <T ES.Generator
+   >-- ES.Generator.HasSelectedElement --> DIA.Element <R L0.HasProperty : L0.Relation
+   @L0.property ES.Generator.HasSelectedElement
+   
+ES.Generator.Model <T ES.Generator
+ES.Generator.Diagram <T ES.Generator
+    >-- ES.Generator.HasDiagram --> DIA.Diagram <R L0.HasProperty : L0.Relation
+    @L0.property ES.Generator.HasDiagram
+
+ES.Selector.All <T ES.Selector
+
+ES.PropertySelector <T ES.Selector
+    >-- ES.PropertySelector.HasSelectionPropertyName --> L0.String <R L0.HasProperty : L0.TotalFunction
+    >-- ES.PropertySelector.HasResultCount --> L0.Integer <R L0.HasProperty : L0.FunctionalRelation
+    @L0.singleProperty ES.PropertySelector.HasSelectionPropertyName
+    @L0.singleProperty ES.PropertySelector.HasResultCount
+
+ES.Selector.NLowest <T ES.PropertySelector
+ES.Selector.NHighest <T ES.PropertySelector
+
+ES.Condition
+    >-- ES.Condition.IsInverse --> ES.Condition <R L0.HasProperty : L0.Tag
+
+ES.AggregateCondition <T ES.Condition
+   >-- ES.HasSubcondition --> ES.Selector <R L0.IsComposedOf : L0.Relation
+   @L0.property ES.HasSubcondition
+   
+// Logical and
+ES.Conjunction <T ES.AggregateCondition
+// Logical or
+ES.Disjunction <T ES.AggregateCondition
+// Logical nor
+ES.Negation <T ES.AggregateCondition
+
+ES.ElementaryCondition <T ES.Condition
+
+ES.PropertyCondition <T ES.ElementaryCondition
+    >-- ES.PropertyCondition.HasPropertyName --> L0.String <R L0.HasProperty : L0.TotalFunction
+    >-- ES.PropertyCondition.HasLowerLimit --> L0.Double <R L0.HasProperty : L0.FunctionalRelation
+    >-- ES.PropertyCondition.HasUpperLimit --> L0.Double <R L0.HasProperty : L0.FunctionalRelation
+    @L0.singleProperty ES.PropertyCondition.HasPropertyName
+    @L0.optionalProperty ES.PropertyCondition.HasLowerLimit
+    @L0.optionalProperty ES.PropertyCondition.HasUpperLimit
+
+ES.RouteCondition <T ES.ElementaryCondition
+    >-- ES.RouteCondition.HasRoute --> DNR.Route <R L0.HasProperty : L0.TotalFunction
+    @L0.singleProperty ES.RouteCondition.HasRoute
+ES.RegionCondition <T ES.ElementaryCondition
+    >-- ES.RegionCondition.HasRegion --> REG.Region <R L0.HasProperty : L0.TotalFunction
+    @L0.singleProperty ES.RouteCondition.HasRoute
diff --git a/org.simantics.district.selection/pom.xml b/org.simantics.district.selection/pom.xml
new file mode 100644 (file)
index 0000000..758be94
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+       xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.simantics.district</groupId>
+               <artifactId>org.simantics.district.root</artifactId>
+               <version>1.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>org.simantics.district.selection</artifactId>
+       <packaging>eclipse-plugin</packaging>
+       <version>1.0.0-SNAPSHOT</version>
+
+</project>
diff --git a/org.simantics.district.selection/scl/Simantics/District/Selection.scl b/org.simantics.district.selection/scl/Simantics/District/Selection.scl
new file mode 100644 (file)
index 0000000..4f1d270
--- /dev/null
@@ -0,0 +1,89 @@
+import "http://www.simantics.org/ElementSelection-1.0" as ES
+import "Simantics/DB"
+
+importJava "org.simantics.district.selection.ElementSelector" where
+    data ElementSelector
+
+    """
+    Get a selection resource represented as an ElementSelector object.
+    """
+    getSelector :: Resource -> <ReadGraph> ElementSelector
+    
+    """
+    Get the name of the selection from an element selector object.
+    """
+    @JavaName getName
+    getSelectorName :: ElementSelector -> String
+    
+    """
+    Get an expression that represents the selection from an element selector object.
+    """
+    @JavaName getExpression
+    getSelectorExpression :: ElementSelector -> String
+    
+    """
+    Get the selection resource form an element selector object.
+    """
+    @JavaName getResource
+    getSelectorResource :: ElementSelector -> Resource
+
+    """
+    Get the elements selected by an element selector object.
+
+    `selectedElements = selectElementsFrom elementSelector model`
+    """
+    @JavaName selectElementsFrom
+    selectElementsFrom :: ElementSelector -> Resource -> <ReadGraph> [Resource]
+
+data Generator = Model | Diagram Resource | Explicit [Resource]
+data Selector = All | NLowest String Integer | NHighest String Integer
+data Condition = PropertyCondition String (Maybe Double) (Maybe Double)
+               | Region Resource | Route Resource
+               | Conjunction [Condition] | Disjunction [Condition] | Negation [Condition]
+               | Not Condition
+data Selection = Selection Generator Selector (Maybe Condition)
+
+"""
+Write a selection expression into the database.
+
+`selectionResource = writeSelection parentResource selectionName selection`
+"""
+writeSelection :: Resource -> String -> Selection -> <WriteGraph> Resource
+writeSelection root name (Selection generator selector maybeCondition) = let
+    selection = newEntity [hasName name, hasLabel name, hasType ES.Selection, hasParent root]
+    generator = match generator with
+        Model -> newEntity [hasType ES.Generator.Model]
+        Diagram diagram -> newEntity [hasType ES.Generator.Diagram, hasStatement ES.Generator.HasDiagram diagram]
+        Explicit elements -> let
+            g = newEntity [hasType ES.Generator.Explicit]
+            for elements (claim g ES.Generator.HasSelectedElement)
+            in g
+    claim selection ES.Selection.HasGenerator generator
+    selector = match selector with
+        All -> newEntity [hasType ES.Selector.All]
+        NLowest propertyName count -> newEntity [hasType ES.Selector.NLowest,
+                                                 hasProperty ES.PropertySelector.HasSelectionPropertyName propertyName,
+                                                 hasProperty ES.PropertySelector.HasResultCount count]
+        NHighest propertyName count -> newEntity [hasType ES.Selector.NHighest,
+                                                  hasProperty ES.PropertySelector.HasSelectionPropertyName propertyName,
+                                                  hasProperty ES.PropertySelector.HasResultCount count]
+    claim selection ES.Selection.HasSelector selector
+    for maybeCondition \condition -> claim selection ES.Selection.HasCondition $ writeCondition condition
+    in selection
+  where
+    writeCondition condition = match condition with
+            PropertyCondition propertyName lowerLimit upperLimit -> let
+                cond = newEntity [hasType ES.PropertyCondition, hasProperty ES.PropertyCondition.HasPropertyName propertyName]
+                for lowerLimit \limit -> claimRelatedValue cond ES.PropertyCondition.HasLowerLimit limit
+                for upperLimit \limit -> claimRelatedValue cond ES.PropertyCondition.HasUpperLimit limit
+                in cond
+            Region region -> newEntity [hasType ES.RegionCondition, hasProperty ES.RegionCondition.HasRegion region]
+            Route route -> newEntity [hasType ES.RouteCondition, hasProperty ES.RouteCondition.HasRoute route]
+            Conjunction conds -> writeAggregateCondition ES.Conjunction conds
+            Disjunction conds -> writeAggregateCondition ES.Disjunction conds
+            Negation conds -> writeAggregateCondition ES.Negation conds
+            Not cond -> writeAggregateCondition ES.Negation [cond]
+    writeAggregateCondition condType conds = let
+        cond = newEntity [hasType condType]
+        for conds \c -> claim cond ES.HasSubcondition $ writeCondition c
+        in cond
diff --git a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionResource.java b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionResource.java
new file mode 100644 (file)
index 0000000..e75673d
--- /dev/null
@@ -0,0 +1,196 @@
+package org.simantics.district.selection;
+
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.request.Read;
+import org.simantics.db.Session;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.service.QueryControl;
+
+public class ElementSelectionResource {
+    
+    public final Resource AggregateCondition;
+    public final Resource Condition;
+    public final Resource Condition_IsInverse;
+    public final Resource Condition_IsInverse_Inverse;
+    public final Resource Conjunction;
+    public final Resource Disjunction;
+    public final Resource ElementaryCondition;
+    public final Resource Generator;
+    public final Resource Generator_Diagram;
+    public final Resource Generator_Explicit;
+    public final Resource Generator_HasDiagram;
+    public final Resource Generator_HasDiagram_Inverse;
+    public final Resource Generator_HasSelectedElement;
+    public final Resource Generator_HasSelectedElement_Inverse;
+    public final Resource Generator_Model;
+    public final Resource HasSubcondition;
+    public final Resource HasSubcondition_Inverse;
+    public final Resource Negation;
+    public final Resource PropertyCondition;
+    public final Resource PropertyCondition_HasLowerLimit;
+    public final Resource PropertyCondition_HasLowerLimit_Inverse;
+    public final Resource PropertyCondition_HasPropertyName;
+    public final Resource PropertyCondition_HasPropertyName_Inverse;
+    public final Resource PropertyCondition_HasUpperLimit;
+    public final Resource PropertyCondition_HasUpperLimit_Inverse;
+    public final Resource PropertySelector;
+    public final Resource PropertySelector_HasResultCount;
+    public final Resource PropertySelector_HasResultCount_Inverse;
+    public final Resource PropertySelector_HasSelectionPropertyName;
+    public final Resource PropertySelector_HasSelectionPropertyName_Inverse;
+    public final Resource RegionCondition;
+    public final Resource RegionCondition_HasRegion;
+    public final Resource RegionCondition_HasRegion_Inverse;
+    public final Resource RouteCondition;
+    public final Resource RouteCondition_HasRoute;
+    public final Resource RouteCondition_HasRoute_Inverse;
+    public final Resource Selection;
+    public final Resource Selection_HasCondition;
+    public final Resource Selection_HasCondition_Inverse;
+    public final Resource Selection_HasGenerator;
+    public final Resource Selection_HasGenerator_Inverse;
+    public final Resource Selection_HasSelector;
+    public final Resource Selection_HasSelector_Inverse;
+    public final Resource Selector;
+    public final Resource Selector_All;
+    public final Resource Selector_NHighest;
+    public final Resource Selector_NLowest;
+        
+    public static class URIs {
+        public static final String AggregateCondition = "http://www.simantics.org/ElementSelection-1.0/AggregateCondition";
+        public static final String Condition = "http://www.simantics.org/ElementSelection-1.0/Condition";
+        public static final String Condition_IsInverse = "http://www.simantics.org/ElementSelection-1.0/Condition/IsInverse";
+        public static final String Condition_IsInverse_Inverse = "http://www.simantics.org/ElementSelection-1.0/Condition/IsInverse/Inverse";
+        public static final String Conjunction = "http://www.simantics.org/ElementSelection-1.0/Conjunction";
+        public static final String Disjunction = "http://www.simantics.org/ElementSelection-1.0/Disjunction";
+        public static final String ElementaryCondition = "http://www.simantics.org/ElementSelection-1.0/ElementaryCondition";
+        public static final String Generator = "http://www.simantics.org/ElementSelection-1.0/Generator";
+        public static final String Generator_Diagram = "http://www.simantics.org/ElementSelection-1.0/Generator/Diagram";
+        public static final String Generator_Explicit = "http://www.simantics.org/ElementSelection-1.0/Generator/Explicit";
+        public static final String Generator_HasDiagram = "http://www.simantics.org/ElementSelection-1.0/Generator/HasDiagram";
+        public static final String Generator_HasDiagram_Inverse = "http://www.simantics.org/ElementSelection-1.0/Generator/HasDiagram/Inverse";
+        public static final String Generator_HasSelectedElement = "http://www.simantics.org/ElementSelection-1.0/Generator/HasSelectedElement";
+        public static final String Generator_HasSelectedElement_Inverse = "http://www.simantics.org/ElementSelection-1.0/Generator/HasSelectedElement/Inverse";
+        public static final String Generator_Model = "http://www.simantics.org/ElementSelection-1.0/Generator/Model";
+        public static final String HasSubcondition = "http://www.simantics.org/ElementSelection-1.0/HasSubcondition";
+        public static final String HasSubcondition_Inverse = "http://www.simantics.org/ElementSelection-1.0/HasSubcondition/Inverse";
+        public static final String Negation = "http://www.simantics.org/ElementSelection-1.0/Negation";
+        public static final String PropertyCondition = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition";
+        public static final String PropertyCondition_HasLowerLimit = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasLowerLimit";
+        public static final String PropertyCondition_HasLowerLimit_Inverse = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasLowerLimit/Inverse";
+        public static final String PropertyCondition_HasPropertyName = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasPropertyName";
+        public static final String PropertyCondition_HasPropertyName_Inverse = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasPropertyName/Inverse";
+        public static final String PropertyCondition_HasUpperLimit = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasUpperLimit";
+        public static final String PropertyCondition_HasUpperLimit_Inverse = "http://www.simantics.org/ElementSelection-1.0/PropertyCondition/HasUpperLimit/Inverse";
+        public static final String PropertySelector = "http://www.simantics.org/ElementSelection-1.0/PropertySelector";
+        public static final String PropertySelector_HasResultCount = "http://www.simantics.org/ElementSelection-1.0/PropertySelector/HasResultCount";
+        public static final String PropertySelector_HasResultCount_Inverse = "http://www.simantics.org/ElementSelection-1.0/PropertySelector/HasResultCount/Inverse";
+        public static final String PropertySelector_HasSelectionPropertyName = "http://www.simantics.org/ElementSelection-1.0/PropertySelector/HasSelectionPropertyName";
+        public static final String PropertySelector_HasSelectionPropertyName_Inverse = "http://www.simantics.org/ElementSelection-1.0/PropertySelector/HasSelectionPropertyName/Inverse";
+        public static final String RegionCondition = "http://www.simantics.org/ElementSelection-1.0/RegionCondition";
+        public static final String RegionCondition_HasRegion = "http://www.simantics.org/ElementSelection-1.0/RegionCondition/HasRegion";
+        public static final String RegionCondition_HasRegion_Inverse = "http://www.simantics.org/ElementSelection-1.0/RegionCondition/HasRegion/Inverse";
+        public static final String RouteCondition = "http://www.simantics.org/ElementSelection-1.0/RouteCondition";
+        public static final String RouteCondition_HasRoute = "http://www.simantics.org/ElementSelection-1.0/RouteCondition/HasRoute";
+        public static final String RouteCondition_HasRoute_Inverse = "http://www.simantics.org/ElementSelection-1.0/RouteCondition/HasRoute/Inverse";
+        public static final String Selection = "http://www.simantics.org/ElementSelection-1.0/Selection";
+        public static final String Selection_HasCondition = "http://www.simantics.org/ElementSelection-1.0/Selection/HasCondition";
+        public static final String Selection_HasCondition_Inverse = "http://www.simantics.org/ElementSelection-1.0/Selection/HasCondition/Inverse";
+        public static final String Selection_HasGenerator = "http://www.simantics.org/ElementSelection-1.0/Selection/HasGenerator";
+        public static final String Selection_HasGenerator_Inverse = "http://www.simantics.org/ElementSelection-1.0/Selection/HasGenerator/Inverse";
+        public static final String Selection_HasSelector = "http://www.simantics.org/ElementSelection-1.0/Selection/HasSelector";
+        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_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";
+    }
+    
+    public static Resource getResourceOrNull(ReadGraph graph, String uri) {
+        try {
+            return graph.getResource(uri);
+        } catch(DatabaseException e) {
+            System.err.println(e.getMessage());
+            return null;
+        }
+    }
+    
+    public ElementSelectionResource(ReadGraph graph) {
+        AggregateCondition = getResourceOrNull(graph, URIs.AggregateCondition);
+        Condition = getResourceOrNull(graph, URIs.Condition);
+        Condition_IsInverse = getResourceOrNull(graph, URIs.Condition_IsInverse);
+        Condition_IsInverse_Inverse = getResourceOrNull(graph, URIs.Condition_IsInverse_Inverse);
+        Conjunction = getResourceOrNull(graph, URIs.Conjunction);
+        Disjunction = getResourceOrNull(graph, URIs.Disjunction);
+        ElementaryCondition = getResourceOrNull(graph, URIs.ElementaryCondition);
+        Generator = getResourceOrNull(graph, URIs.Generator);
+        Generator_Diagram = getResourceOrNull(graph, URIs.Generator_Diagram);
+        Generator_Explicit = getResourceOrNull(graph, URIs.Generator_Explicit);
+        Generator_HasDiagram = getResourceOrNull(graph, URIs.Generator_HasDiagram);
+        Generator_HasDiagram_Inverse = getResourceOrNull(graph, URIs.Generator_HasDiagram_Inverse);
+        Generator_HasSelectedElement = getResourceOrNull(graph, URIs.Generator_HasSelectedElement);
+        Generator_HasSelectedElement_Inverse = getResourceOrNull(graph, URIs.Generator_HasSelectedElement_Inverse);
+        Generator_Model = getResourceOrNull(graph, URIs.Generator_Model);
+        HasSubcondition = getResourceOrNull(graph, URIs.HasSubcondition);
+        HasSubcondition_Inverse = getResourceOrNull(graph, URIs.HasSubcondition_Inverse);
+        Negation = getResourceOrNull(graph, URIs.Negation);
+        PropertyCondition = getResourceOrNull(graph, URIs.PropertyCondition);
+        PropertyCondition_HasLowerLimit = getResourceOrNull(graph, URIs.PropertyCondition_HasLowerLimit);
+        PropertyCondition_HasLowerLimit_Inverse = getResourceOrNull(graph, URIs.PropertyCondition_HasLowerLimit_Inverse);
+        PropertyCondition_HasPropertyName = getResourceOrNull(graph, URIs.PropertyCondition_HasPropertyName);
+        PropertyCondition_HasPropertyName_Inverse = getResourceOrNull(graph, URIs.PropertyCondition_HasPropertyName_Inverse);
+        PropertyCondition_HasUpperLimit = getResourceOrNull(graph, URIs.PropertyCondition_HasUpperLimit);
+        PropertyCondition_HasUpperLimit_Inverse = getResourceOrNull(graph, URIs.PropertyCondition_HasUpperLimit_Inverse);
+        PropertySelector = getResourceOrNull(graph, URIs.PropertySelector);
+        PropertySelector_HasResultCount = getResourceOrNull(graph, URIs.PropertySelector_HasResultCount);
+        PropertySelector_HasResultCount_Inverse = getResourceOrNull(graph, URIs.PropertySelector_HasResultCount_Inverse);
+        PropertySelector_HasSelectionPropertyName = getResourceOrNull(graph, URIs.PropertySelector_HasSelectionPropertyName);
+        PropertySelector_HasSelectionPropertyName_Inverse = getResourceOrNull(graph, URIs.PropertySelector_HasSelectionPropertyName_Inverse);
+        RegionCondition = getResourceOrNull(graph, URIs.RegionCondition);
+        RegionCondition_HasRegion = getResourceOrNull(graph, URIs.RegionCondition_HasRegion);
+        RegionCondition_HasRegion_Inverse = getResourceOrNull(graph, URIs.RegionCondition_HasRegion_Inverse);
+        RouteCondition = getResourceOrNull(graph, URIs.RouteCondition);
+        RouteCondition_HasRoute = getResourceOrNull(graph, URIs.RouteCondition_HasRoute);
+        RouteCondition_HasRoute_Inverse = getResourceOrNull(graph, URIs.RouteCondition_HasRoute_Inverse);
+        Selection = getResourceOrNull(graph, URIs.Selection);
+        Selection_HasCondition = getResourceOrNull(graph, URIs.Selection_HasCondition);
+        Selection_HasCondition_Inverse = getResourceOrNull(graph, URIs.Selection_HasCondition_Inverse);
+        Selection_HasGenerator = getResourceOrNull(graph, URIs.Selection_HasGenerator);
+        Selection_HasGenerator_Inverse = getResourceOrNull(graph, URIs.Selection_HasGenerator_Inverse);
+        Selection_HasSelector = getResourceOrNull(graph, URIs.Selection_HasSelector);
+        Selection_HasSelector_Inverse = getResourceOrNull(graph, URIs.Selection_HasSelector_Inverse);
+        Selector = getResourceOrNull(graph, URIs.Selector);
+        Selector_All = getResourceOrNull(graph, URIs.Selector_All);
+        Selector_NHighest = getResourceOrNull(graph, URIs.Selector_NHighest);
+        Selector_NLowest = getResourceOrNull(graph, URIs.Selector_NLowest);
+    }
+    
+    public static ElementSelectionResource getInstance(ReadGraph graph) {
+        Session session = graph.getSession();
+        ElementSelectionResource ret = session.peekService(ElementSelectionResource.class);
+        if(ret == null) {
+            QueryControl qc = graph.getService(QueryControl.class);
+            ret = new ElementSelectionResource(qc.getIndependentGraph(graph));
+            session.registerService(ElementSelectionResource.class, ret);
+        }
+        return ret;
+    }
+    
+    public static ElementSelectionResource getInstance(RequestProcessor session) throws DatabaseException {
+        ElementSelectionResource ret = session.peekService(ElementSelectionResource.class);
+        if(ret == null) {
+            ret = session.syncRequest(new Read<ElementSelectionResource>() {
+                public ElementSelectionResource perform(ReadGraph graph) throws DatabaseException {
+                    QueryControl qc = graph.getService(QueryControl.class);
+                    return new ElementSelectionResource(qc.getIndependentGraph(graph));
+                }
+            });
+            session.registerService(ElementSelectionResource.class, ret);
+        }
+        return ret;
+    }
+    
+}
+
diff --git a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java
new file mode 100644 (file)
index 0000000..9de4663
--- /dev/null
@@ -0,0 +1,600 @@
+package org.simantics.district.selection;
+
+import java.awt.geom.Path2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.common.utils.ListUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ServiceException;
+import org.simantics.db.layer0.request.ActiveModels;
+import org.simantics.db.layer0.request.ActiveRuns;
+import org.simantics.db.layer0.request.Configuration;
+import org.simantics.db.layer0.variable.RVI;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.diagram.stubs.DiagramResource;
+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.scl.runtime.Lists;
+import org.simantics.scl.runtime.function.FunctionImpl1;
+import org.simantics.scl.runtime.tuple.Tuple2;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElementSelector {
+       String name;
+       String expression;
+       Integer count;
+       Resource resource;
+       
+       Generator generator;
+       Selector selector;
+       Condition condition;
+       
+       static Logger LOG = LoggerFactory.getLogger(ElementSelector.class);
+       
+       static ElementSelectionResource ES;
+       static Layer0 L0;
+       static StructuralResource2 STR;
+       static ModelingResources MOD;
+       static DiagramResource DIA;
+       
+       ElementSelector(ReadGraph graph, Resource resource) throws DatabaseException {
+               super();
+               
+               L0 = Layer0.getInstance(graph);
+               ES = ElementSelectionResource.getInstance(graph);
+               STR = StructuralResource2.getInstance(graph);
+               MOD = ModelingResources.getInstance(graph);
+               DIA = DiagramResource.getInstance(graph);
+               
+               this.resource = resource;
+               this.count = -1;
+               try {
+                       this.name = graph.getRelatedValue(resource, L0.HasLabel);
+                       this.expression = buildExpression(graph, resource);
+               } catch (DatabaseException e) {
+                       LOG.error("Error reading element selector", e);
+                       throw e;
+               }
+       }
+       
+       public static ElementSelector getSelector(ReadGraph graph, Resource resource) throws DatabaseException {
+               return graph.syncRequest(new ElementSelectorQuery(resource));
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public String getExpression() {
+               return expression;
+       }
+
+       public Resource getResource() {
+               return resource;
+       }
+
+       public List<Resource> selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException {
+               if (selector == null) {
+                       buildSelection(graph);
+               }
+               
+               Collection<Resource> result = gather(graph, model);
+               return result instanceof List ? (List<Resource>) result : new ArrayList<>(result);
+       }
+
+       private static Generator buildGenerator(ReadGraph graph, Resource resource) throws DatabaseException {
+               if (!graph.isInstanceOf(resource, ES.Generator))
+                       throw new IllegalArgumentException("Resource " + resource + " is not a valid generator");
+               
+               if (graph.isInstanceOf(resource, ES.Generator_Model)) {
+                       return new ModelGenerator();
+               }
+               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)) {
+                       return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement));
+               }
+               else {
+                       throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource)));
+               }
+       }
+
+       private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException {
+               if (!graph.isInstanceOf(resource, ES.Selector))
+                       throw new IllegalArgumentException("Resource " + resource + " is not a valid selector");
+               
+               Selector s;
+               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 {
+                       throw new IllegalArgumentException("Unknown selector type " + graph.getURI(graph.getSingleType(resource)));
+               }
+               return s;
+       }
+
+       private static Condition buildCondition(ReadGraph graph, Resource resource) throws DatabaseException {
+               if (resource == null)
+                       return null;
+               
+               if (!graph.isInstanceOf(resource, ES.Condition))
+                       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(propertyName, lowerLimit, upperLimit);
+               }
+               else if (graph.isInstanceOf(resource, ES.RegionCondition)) {
+                       DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
+                       double[] region = graph.getRelatedValue(graph.getSingleObject(resource, ES.RegionCondition_HasRegion), DR.Region_area);
+                       cond = new RegionCondition(region);
+               }
+               else if (graph.isInstanceOf(resource, ES.RouteCondition)) {
+                       Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, graph.getSingleObject(resource, ES.RouteCondition_HasRoute)));
+                       cond = new RouteCondition(routePoints);
+               }
+               else if (graph.isInstanceOf(resource, ES.AggregateCondition)) {
+                       boolean isDisjunction = graph.isInstanceOf(resource, ES.Disjunction);
+                       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(type, conditions);
+               }
+               else {
+                       throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
+               }
+               
+               cond.isInverse = graph.hasStatement(resource, ES.Condition_IsInverse, resource);
+               return cond;
+       }
+
+       private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
+               if (graph.isInstanceOf(r, ES.Selection)) {
+                       String exp = "select " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
+                                    " from " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
+                       
+                       Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
+                       return cond != null ? exp + " where {" + buildExpression(graph, cond) + "}" : exp;
+               }
+               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 = buildExpression(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.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.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 {
+                       throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
+               }
+       }
+
+       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 {
+                       StringBuilder result = new StringBuilder();
+                       if (lowerLimit != null) {
+                               result.append(lowerLimit);
+                               result.append(" < ");
+                       }
+                       result.append(propertyName);
+                       if (upperLimit != null) {
+                               result.append(" < ");
+                               result.append(upperLimit);
+                       }
+                       return result.toString();
+               }
+       }
+
+       private static Collection<Resource> elementsOfDiagram(ReadGraph graph, Resource diagram) throws ServiceException {
+               Collection<Resource> elements = graph.getObjects(diagram, L0.ConsistsOf);
+               Collection<Resource> result = new ArrayList<>();
+               for (Resource r : elements)
+                       if (graph.isInstanceOf(r, DIA.Element))
+                               result .add(r);
+               return result;
+       }
+
+       private void buildSelection(ReadGraph graph) throws DatabaseException {
+               Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector);
+               Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator);
+               Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition);
+               
+               this.selector = buildSelector(graph, selector);
+               this.generator = buildGenerator(graph, generator);
+               this.condition = buildCondition(graph, condition);
+       }
+
+       private Collection<Resource> filterElementsFrom(ReadGraph graph, Collection<Resource> elements) throws DatabaseException {
+               if (condition == null) return elements;
+               
+               ArrayList<Resource> result = new ArrayList<>();
+               for (Resource r : elements) {
+                       if (condition.match(graph, r))
+                               result.add(r);
+               }
+               
+               return result;
+       }
+       
+       private Collection<Resource> gather(ReadGraph graph, Resource model) throws DatabaseException {
+               return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model)));
+       }
+       
+       static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException {
+               Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);
+               if (component != null) {
+                       Variable var = Variables.getVariable(graph, component);
+                       RVI realRvi = var.getRVI(graph);
+                       
+                       for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) {
+                               for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) {
+                                       Variable v = realRvi.resolvePossible(graph, run);
+                                       if (v != null) {
+                                               return v;
+                                       }
+                               }
+                               Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel);
+                               if (configuration != null) {
+                                       Variable v = realRvi.resolvePossible(graph, configuration);
+                                       if (v != null) {
+                                               return v;
+                                       }
+                               }
+                       }
+               }
+               
+               return null;
+       }
+       
+       static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) {
+               try {
+                       Variable v = getVariableForElement(graph, element);
+                       if (v != null) {
+                               Number value = v.getPossiblePropertyValue(graph, propertyName);
+                               if (value != null)
+                                       return value.doubleValue();
+                       }
+               }
+               catch (DatabaseException e) {
+               }
+               
+               return null;
+       }
+       
+       // Generators
+       
+       static abstract class Generator {
+               abstract Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException;
+       }
+       
+       static class ModelGenerator extends Generator {
+               @Override
+               Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
+                       Resource conf = graph.syncRequest(new Configuration(model));
+                       
+                       ArrayList<Resource> result = new ArrayList<>();
+                       
+                       // Iterate over diagrams
+                       for (Resource comp : graph.getObjects(conf, L0.ConsistsOf)) {
+                               if (!graph.isInstanceOf(comp, STR.Composite)) continue;
+                               
+                               Resource diagram = graph.getPossibleObject(comp, MOD.CompositeToDiagram);
+                               if (diagram != null) result.addAll(elementsOfDiagram(graph, diagram));
+                       }
+                       
+                       return result;
+               }
+       }
+       
+       static class DiagramGenerator extends Generator {
+               Resource diagram;
+               
+               public DiagramGenerator(Resource diagram) {
+                       this.diagram = diagram;
+               }
+
+               @Override
+               Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
+                       return elementsOfDiagram(graph, diagram);
+               }
+       }
+       
+       static class ExplicitGenerator extends Generator {
+               public ExplicitGenerator(Collection<Resource> elements) {
+                       this.elements = elements;
+               }
+
+               Collection<Resource> elements;
+
+               @Override
+               Collection<Resource> generate(ReadGraph graph, Resource model) {
+                       return elements;
+               }
+       }
+       
+       // Selectors
+       
+       static abstract class Selector {
+               abstract Collection<Resource> select(ReadGraph graph, Collection<Resource> elements);
+       }
+       
+       static class All extends Selector {
+               public All() {
+               }
+
+               @Override
+               Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
+                       return elements;
+               }
+       }
+       
+       static class PropertySelector extends Selector {
+               public PropertySelector(boolean smallest, String propertyName, int resultCount) {
+                       this.smallest = smallest;
+                       this.propertyName = propertyName;
+                       this.resultCount = resultCount;
+               }
+               
+               boolean smallest;
+               String propertyName;
+               int resultCount;
+               
+               @SuppressWarnings("unchecked")
+               @Override
+               Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
+                       List<Resource> result = new ArrayList<>(elements);
+                       List<Tuple2> result2 = Lists.map(new FunctionImpl1<Resource, Tuple2>() {
+                               @Override
+                               public Tuple2 apply(Resource r) {
+                                       return new Tuple2(r, getPropertyValue(graph, r, propertyName));
+                               }
+                       }, result);
+                       
+                       result2 = Lists.filter(new FunctionImpl1<Tuple2, Boolean>() {
+                               @Override
+                               public Boolean apply(Tuple2 t) {
+                                       return t.c1 != null;
+                               }
+                       }, result2);
+                       
+                       result2.sort((t1, t2) -> smallest ? Double.compare((Double) t1.c1, (Double) t2.c1) : Double.compare((Double) t2.c1, (Double) t1.c1));
+                       
+                       result2 = result2.subList(0, resultCount);
+                       
+                       result = Lists.map(new FunctionImpl1<Tuple2, Resource>() {
+                               @Override
+                               public Resource apply(Tuple2 p0) {
+                                       return (Resource) p0.c0;
+                               }
+                       }, result2);
+                       
+                       return result;
+               }
+       }
+       
+       // Conditions
+       
+       static abstract class Condition {
+               boolean isInverse;
+               abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
+       }
+       
+       static class PropertyCondition extends Condition {
+               public PropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
+                       super();
+                       this.propertyName = propertyName;
+                       this.lowerLimit = lowerLimit;
+                       this.upperLimit = upperLimit;
+               }
+               
+               String propertyName;
+               Double lowerLimit;
+               Double upperLimit;
+               
+               @Override
+               boolean match(ReadGraph graph, Resource r) {
+                       Double value = getPropertyValue(graph, r, propertyName);
+                       return value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit);
+               }
+       }
+       
+       static class RegionCondition extends Condition {
+               public RegionCondition(double[] region) {
+                       super();
+                       this.region = 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();
+
+                       this.path = path;
+               }
+
+               double[] region;
+               Path2D path;
+
+               @Override
+               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);
+               } 
+       }
+       
+       static class RouteCondition extends Condition {
+               public RouteCondition(Set<Resource> routePoints) {
+                       super();
+                       this.routePoints = routePoints;
+               }
+
+               Set<Resource> routePoints;
+
+               @Override
+               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+                       return routePoints.contains(r);
+               }
+       }
+       
+       static class DiagramCondition extends Condition {
+               public DiagramCondition(Resource diagram) {
+                       super();
+                       this.diagram = diagram;
+               }
+
+               Resource diagram;
+
+               @Override
+               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+                       return graph.getSingleObject(r, L0.PartOf).equals(diagram);
+               }
+       }
+       
+       static class AggregateCondition extends Condition {
+               static enum Type { DISJUNCTION, CONJUNCTION, NEGATION };
+               
+               public AggregateCondition(Type type, List<Condition> conditions) {
+                       super();
+                       this.type = type;
+                       this.conditions = conditions;
+               }
+               
+               Type type;
+               List<Condition> conditions;
+               
+               @Override
+               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
+                       switch (type) {
+                       case DISJUNCTION:
+                               for (Condition c : conditions)
+                                       if (c.match(graph, r)) return true;
+                               return false;
+                       case CONJUNCTION:
+                               for (Condition c : conditions)
+                                       if (!c.match(graph, r)) return false;
+                               return true;
+                       case NEGATION:
+                               for (Condition c : conditions)
+                                       if (c.match(graph, r)) return false;
+                               return true;
+                       default:
+                               // Should not happen
+                               throw new IllegalArgumentException("Unknown aggregate condition type " + type);
+                       }
+               }
+       }
+       
+       public static class ElementSelectorQuery extends ResourceRead<ElementSelector> {
+               public ElementSelectorQuery(Resource resource) {
+                       super(resource);
+               }
+               
+               @Override
+               public ElementSelector perform(ReadGraph graph) throws DatabaseException {
+                       return new ElementSelector(graph, resource);
+               }
+       }
+}
diff --git a/pom.xml b/pom.xml
index 65e92a3b92d4421a98f036f8dedd695e778440d7..50cb2866f8026feb71141d10465dd6db442ac721 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -95,6 +95,7 @@
                <module>org.simantics.district.route</module>
                <module>org.simantics.district.route.ontology</module>
                <module>org.simantics.district.route.ui</module>
+               <module>org.simantics.district.selection</module>
                <module>org.simantics.maps.server</module>
                <module>org.simantics.maps.server.ui</module>