1 package org.simantics.district.selection;
3 import java.awt.geom.Path2D;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.HashSet;
10 import org.simantics.Simantics;
11 import org.simantics.db.ReadGraph;
12 import org.simantics.db.Resource;
13 import org.simantics.db.common.request.ResourceRead;
14 import org.simantics.db.common.utils.ListUtils;
15 import org.simantics.db.exception.DatabaseException;
16 import org.simantics.db.layer0.request.ActiveModels;
17 import org.simantics.db.layer0.request.ActiveRuns;
18 import org.simantics.db.layer0.request.Configuration;
19 import org.simantics.db.layer0.variable.RVI;
20 import org.simantics.db.layer0.variable.Variable;
21 import org.simantics.db.layer0.variable.Variables;
22 import org.simantics.diagram.stubs.DiagramResource;
23 import org.simantics.district.network.ontology.DistrictNetworkResource;
24 import org.simantics.district.region.ontology.DiagramRegionsResource;
25 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
26 import org.simantics.layer0.Layer0;
27 import org.simantics.modeling.ModelingResources;
28 import org.simantics.scl.runtime.Lists;
29 import org.simantics.scl.runtime.function.FunctionImpl1;
30 import org.simantics.scl.runtime.tuple.Tuple2;
31 import org.simantics.structural.stubs.StructuralResource2;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 public class ElementSelector {
45 static Logger LOG = LoggerFactory.getLogger(ElementSelector.class);
47 static ElementSelectionResource ES;
49 static StructuralResource2 STR;
50 static ModelingResources MOD;
51 static DiagramResource DIA;
52 static DistrictNetworkResource DN;
54 ElementSelector(ReadGraph graph, Resource resource) throws DatabaseException {
57 L0 = Layer0.getInstance(graph);
58 ES = ElementSelectionResource.getInstance(graph);
59 STR = StructuralResource2.getInstance(graph);
60 MOD = ModelingResources.getInstance(graph);
61 DIA = DiagramResource.getInstance(graph);
62 DN = DistrictNetworkResource.getInstance(graph);
64 this.resource = resource;
67 this.name = graph.getRelatedValue(resource, L0.HasLabel);
68 this.expression = buildExpression(graph, resource);
69 } catch (DatabaseException e) {
70 LOG.error("Error reading element selector", e);
75 public static ElementSelector getSelector(ReadGraph graph, Resource resource) throws DatabaseException {
76 return graph.syncRequest(new ElementSelectorQuery(resource));
79 public String getName() {
83 public String getExpression() {
87 public Resource getResource() {
91 public List<Resource> selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException {
92 if (selector == null) {
93 buildSelection(graph);
96 Collection<Resource> result = gather(graph, model);
97 return result instanceof List ? (List<Resource>) result : new ArrayList<>(result);
100 private static Generator buildGenerator(ReadGraph graph, Resource resource) throws DatabaseException {
101 if (!graph.isInstanceOf(resource, ES.Generator))
102 throw new IllegalArgumentException("Resource " + resource + " is not a valid generator");
104 if (graph.isInstanceOf(resource, ES.Generator_Model)) {
105 return new ModelGenerator();
107 else if (graph.isInstanceOf(resource, ES.Generator_Diagram)) {
108 return new DiagramGenerator(graph.getSingleObject(resource, ES.Generator_HasDiagram));
110 else if (graph.isInstanceOf(resource, ES.Generator_Explicit)) {
111 return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement));
114 throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource)));
118 private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException {
119 if (!graph.isInstanceOf(resource, ES.Selector))
120 throw new IllegalArgumentException("Resource " + resource + " is not a valid selector");
123 if (graph.isInstanceOf(resource, ES.Selector_All)) {
126 else if (graph.isInstanceOf(resource, ES.PropertySelector)) {
127 String propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName);
128 Integer resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount);
129 boolean isSmallest = graph.isInstanceOf(resource, ES.Selector_NLowest);
130 s = new PropertySelector(isSmallest, propertyName, resultCount);
133 throw new IllegalArgumentException("Unknown selector type " + graph.getURI(graph.getSingleType(resource)));
138 private static Condition buildCondition(ReadGraph graph, Resource resource) throws DatabaseException {
139 if (resource == null)
142 if (!graph.isInstanceOf(resource, ES.Condition))
143 throw new IllegalArgumentException("Resource " + resource + " is not a valid condition");
146 if (graph.isInstanceOf(resource, ES.PropertyCondition)) {
147 String propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName);
148 Double lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit);
149 Double upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit);
150 cond = new PropertyCondition(propertyName, lowerLimit, upperLimit);
152 else if (graph.isInstanceOf(resource, ES.RegionCondition)) {
153 DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
154 double[] region = graph.getRelatedValue(graph.getSingleObject(resource, ES.RegionCondition_HasRegion), DR.Region_area);
155 cond = new RegionCondition(region);
157 else if (graph.isInstanceOf(resource, ES.RouteCondition)) {
158 Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, graph.getSingleObject(resource, ES.RouteCondition_HasRoute)));
159 cond = new RouteCondition(routePoints);
161 else if (graph.isInstanceOf(resource, ES.AggregateCondition)) {
162 boolean isDisjunction = graph.isInstanceOf(resource, ES.Disjunction);
163 Collection<Resource> conditionResources = graph.getObjects(resource, ES.HasSubcondition);
164 List<Condition> conditions = new ArrayList<>(conditionResources.size());
165 for (Resource c : conditionResources) {
166 conditions.add(buildCondition(graph, c));
169 if (graph.isInstanceOf(resource, ES.Conjunction))
170 type = AggregateCondition.Type.CONJUNCTION;
171 else if (graph.isInstanceOf(resource, ES.Negation))
172 type = AggregateCondition.Type.NEGATION;
173 else if (graph.isInstanceOf(resource, ES.Disjunction))
174 type = AggregateCondition.Type.DISJUNCTION;
176 throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource)));
178 cond = new AggregateCondition(type, conditions);
181 throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
184 cond.isInverse = graph.hasStatement(resource, ES.Condition_IsInverse, resource);
188 private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
189 if (graph.isInstanceOf(r, ES.Selection)) {
190 String exp = "select " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) +
191 " from " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
193 Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
194 return cond != null ? exp + " where {" + buildExpression(graph, cond) + "}" : exp;
196 else if (graph.isInstanceOf(r, ES.Condition)) {
197 if (graph.isInstanceOf(r, ES.PropertyCondition)) {
198 return buildPropertyConditionExpression(graph, r);
200 else if (graph.isInstanceOf(r, ES.RegionCondition)) {
201 Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion);
202 String name = graph.getRelatedValue(region, L0.HasLabel);
203 return "in region " + name;
205 else if (graph.isInstanceOf(r, ES.RouteCondition)) {
206 Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute);
207 String name = graph.getRelatedValue(route, L0.HasLabel);
208 return "in route " + name;
210 else if (graph.isInstanceOf(r, ES.AggregateCondition)) {
211 String op = graph.isInstanceOf(r, ES.Conjunction) ? " and " : " or ";
212 List<String> exps = new ArrayList<>();
213 Collection<Resource> objects = graph.getObjects(r, ES.HasSubcondition);
214 for (Resource c : objects) {
215 String exp = buildExpression(graph, c);
216 exps.add(objects.size() > 1 ? "{" + exp + "}" : exp);
218 String result = String.join(op, exps);
219 if (graph.isInstanceOf(r, ES.Negation))
220 result = "not {" + result + "}";
224 throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
227 else if (graph.isInstanceOf(r, ES.Selector)) {
228 if (graph.isInstanceOf(r, ES.Selector_All)) {
231 else if (graph.isInstanceOf(r, ES.PropertySelector)) {
233 if (graph.isInstanceOf(r, ES.Selector_NLowest))
235 else if (graph.isInstanceOf(r, ES.Selector_NHighest))
238 throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
240 String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName);
241 Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount);
242 return op + " " + count + " of " + name;
245 throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
248 else if (graph.isInstanceOf(r, ES.Generator)) {
249 if (graph.isInstanceOf(r, ES.Generator_Model)) {
252 else if (graph.isInstanceOf(r, ES.Generator_Diagram)) {
253 return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\"";
255 else if (graph.isInstanceOf(r, ES.Generator_Explicit)) {
256 return "<list of " + graph.getObjects(r, ES.Generator_HasSelectedElement).size() + " elements>";
259 throw new DatabaseException("Unsupported generator resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
263 throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
267 private static String buildPropertyConditionExpression(ReadGraph graph, Resource r) throws DatabaseException {
268 String propertyName = graph.getRelatedValue(r, ES.PropertyCondition_HasPropertyName);
269 Double lowerLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasLowerLimit);
270 Double upperLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasUpperLimit);
271 if (lowerLimit == null && upperLimit == null) {
272 return "has property " + propertyName;
275 StringBuilder result = new StringBuilder();
276 if (lowerLimit != null) {
277 result.append(lowerLimit);
278 result.append(" < ");
280 result.append(propertyName);
281 if (upperLimit != null) {
282 result.append(" < ");
283 result.append(upperLimit);
285 return result.toString();
289 private static Collection<Resource> elementsOfDiagram(ReadGraph graph, Resource diagram) throws DatabaseException {
290 if (graph.isInstanceOf(diagram, STR.Composite)) {
291 // Resource is a composite - get diagram
292 return elementsOfDiagram(graph, graph.getSingleObject(diagram, MOD.CompositeToDiagram));
295 Collection<Resource> elements = graph.getObjects(diagram, L0.ConsistsOf);
296 Collection<Resource> result = new ArrayList<>();
297 for (Resource r : elements)
298 if (graph.isInstanceOf(r, DIA.Element))
303 private void buildSelection(ReadGraph graph) throws DatabaseException {
304 Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector);
305 Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator);
306 Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition);
308 this.selector = buildSelector(graph, selector);
309 this.generator = buildGenerator(graph, generator);
310 this.condition = buildCondition(graph, condition);
313 private Collection<Resource> filterElementsFrom(ReadGraph graph, Collection<Resource> elements) throws DatabaseException {
314 if (condition == null) return elements;
316 ArrayList<Resource> result = new ArrayList<>();
317 for (Resource r : elements) {
318 if (condition.match(graph, r))
325 private Collection<Resource> gather(ReadGraph graph, Resource model) throws DatabaseException {
326 return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model)));
329 static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException {
330 Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);
331 if (component != null) {
332 Variable var = Variables.getVariable(graph, component);
333 RVI realRvi = var.getRVI(graph);
335 for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) {
336 for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) {
337 Variable v = realRvi.resolvePossible(graph, run);
342 Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel);
343 if (configuration != null) {
344 Variable v = realRvi.resolvePossible(graph, configuration);
355 static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) {
357 Variable v = getVariableForElement(graph, element);
359 Number value = v.getPossiblePropertyValue(graph, propertyName);
361 return value.doubleValue();
364 // No property found - try possible mapped element property as well
365 Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent);
366 if (mappedElement != null)
367 return getPropertyValue(graph, mappedElement, propertyName);
369 catch (DatabaseException e) {
377 static abstract class Generator {
378 abstract Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException;
381 static class ModelGenerator extends Generator {
383 Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
384 Resource conf = graph.syncRequest(new Configuration(model));
386 ArrayList<Resource> result = new ArrayList<>();
388 // Iterate over diagrams
389 for (Resource comp : graph.getObjects(conf, L0.ConsistsOf)) {
390 if (!graph.isInstanceOf(comp, STR.Composite)) continue;
392 Resource diagram = graph.getPossibleObject(comp, MOD.CompositeToDiagram);
393 if (diagram != null) result.addAll(elementsOfDiagram(graph, diagram));
400 static class DiagramGenerator extends Generator {
403 public DiagramGenerator(Resource diagram) {
404 this.diagram = diagram;
408 Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
409 return elementsOfDiagram(graph, diagram);
413 static class ExplicitGenerator extends Generator {
414 public ExplicitGenerator(Collection<Resource> elements) {
415 this.elements = elements;
418 Collection<Resource> elements;
421 Collection<Resource> generate(ReadGraph graph, Resource model) {
428 static abstract class Selector {
429 abstract Collection<Resource> select(ReadGraph graph, Collection<Resource> elements);
432 static class All extends Selector {
437 Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
442 static class PropertySelector extends Selector {
443 public PropertySelector(boolean smallest, String propertyName, int resultCount) {
444 this.smallest = smallest;
445 this.propertyName = propertyName;
446 this.resultCount = resultCount;
453 @SuppressWarnings("unchecked")
455 Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
456 List<Resource> result = new ArrayList<>(elements);
457 List<Tuple2> result2 = Lists.map(new FunctionImpl1<Resource, Tuple2>() {
459 public Tuple2 apply(Resource r) {
460 return new Tuple2(r, getPropertyValue(graph, r, propertyName));
464 result2 = Lists.filter(new FunctionImpl1<Tuple2, Boolean>() {
466 public Boolean apply(Tuple2 t) {
471 result2.sort((t1, t2) -> smallest ? Double.compare((Double) t1.c1, (Double) t2.c1) : Double.compare((Double) t2.c1, (Double) t1.c1));
473 if (resultCount < result2.size())
474 result2 = result2.subList(0, resultCount);
476 result = Lists.map(new FunctionImpl1<Tuple2, Resource>() {
478 public Resource apply(Tuple2 p0) {
479 return (Resource) p0.c0;
489 static abstract class Condition {
491 abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
494 static class PropertyCondition extends Condition {
495 public PropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
497 this.propertyName = propertyName;
498 this.lowerLimit = lowerLimit;
499 this.upperLimit = upperLimit;
507 boolean match(ReadGraph graph, Resource r) {
508 Double value = getPropertyValue(graph, r, propertyName);
509 return value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit);
513 static class RegionCondition extends Condition {
514 public RegionCondition(double[] region) {
516 this.region = region;
518 Path2D path = new Path2D.Double();
519 double startX = region[0];
520 double startY = region[1];
521 path.moveTo(startX, startY);
522 for (int i = 2; i < region.length; i+=2)
523 path.lineTo(region[i], region[i+1]);
533 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
534 double[] transform = graph.getRelatedValue(r, DIA.HasTransform);
535 double x = transform[4];
536 double y = transform[5];
537 return path.contains(x, y);
541 static class RouteCondition extends Condition {
542 public RouteCondition(Set<Resource> routePoints) {
544 this.routePoints = routePoints;
547 Set<Resource> routePoints;
550 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
551 return routePoints.contains(r);
555 static class DiagramCondition extends Condition {
556 public DiagramCondition(Resource diagram) {
558 this.diagram = diagram;
564 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
565 return graph.getSingleObject(r, L0.PartOf).equals(diagram);
569 static class AggregateCondition extends Condition {
570 static enum Type { DISJUNCTION, CONJUNCTION, NEGATION };
572 public AggregateCondition(Type type, List<Condition> conditions) {
575 this.conditions = conditions;
579 List<Condition> conditions;
582 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
585 for (Condition c : conditions)
586 if (c.match(graph, r)) return true;
589 for (Condition c : conditions)
590 if (!c.match(graph, r)) return false;
593 for (Condition c : conditions)
594 if (c.match(graph, r)) return false;
598 throw new IllegalArgumentException("Unknown aggregate condition type " + type);
603 public static class ElementSelectorQuery extends ResourceRead<ElementSelector> {
604 public ElementSelectorQuery(Resource resource) {
609 public ElementSelector perform(ReadGraph graph) throws DatabaseException {
610 return new ElementSelector(graph, resource);