]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java
Management of mapped component properties in queries
[simantics/district.git] / org.simantics.district.selection / src / org / simantics / district / selection / ElementSelector.java
1 package org.simantics.district.selection;
2
3 import java.awt.geom.Path2D;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Set;
9
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.exception.ServiceException;
17 import org.simantics.db.layer0.request.ActiveModels;
18 import org.simantics.db.layer0.request.ActiveRuns;
19 import org.simantics.db.layer0.request.Configuration;
20 import org.simantics.db.layer0.variable.RVI;
21 import org.simantics.db.layer0.variable.Variable;
22 import org.simantics.db.layer0.variable.Variables;
23 import org.simantics.diagram.stubs.DiagramResource;
24 import org.simantics.district.network.ontology.DistrictNetworkResource;
25 import org.simantics.district.region.ontology.DiagramRegionsResource;
26 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
27 import org.simantics.layer0.Layer0;
28 import org.simantics.modeling.ModelingResources;
29 import org.simantics.scl.runtime.Lists;
30 import org.simantics.scl.runtime.function.FunctionImpl1;
31 import org.simantics.scl.runtime.tuple.Tuple2;
32 import org.simantics.structural.stubs.StructuralResource2;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 public class ElementSelector {
37         String name;
38         String expression;
39         Integer count;
40         Resource resource;
41         
42         Generator generator;
43         Selector selector;
44         Condition condition;
45         
46         static Logger LOG = LoggerFactory.getLogger(ElementSelector.class);
47         
48         static ElementSelectionResource ES;
49         static Layer0 L0;
50         static StructuralResource2 STR;
51         static ModelingResources MOD;
52         static DiagramResource DIA;
53         static DistrictNetworkResource DN;
54         
55         ElementSelector(ReadGraph graph, Resource resource) throws DatabaseException {
56                 super();
57                 
58                 L0 = Layer0.getInstance(graph);
59                 ES = ElementSelectionResource.getInstance(graph);
60                 STR = StructuralResource2.getInstance(graph);
61                 MOD = ModelingResources.getInstance(graph);
62                 DIA = DiagramResource.getInstance(graph);
63                 DN = DistrictNetworkResource.getInstance(graph);
64                 
65                 this.resource = resource;
66                 this.count = -1;
67                 try {
68                         this.name = graph.getRelatedValue(resource, L0.HasLabel);
69                         this.expression = buildExpression(graph, resource);
70                 } catch (DatabaseException e) {
71                         LOG.error("Error reading element selector", e);
72                         throw e;
73                 }
74         }
75         
76         public static ElementSelector getSelector(ReadGraph graph, Resource resource) throws DatabaseException {
77                 return graph.syncRequest(new ElementSelectorQuery(resource));
78         }
79
80         public String getName() {
81                 return name;
82         }
83
84         public String getExpression() {
85                 return expression;
86         }
87
88         public Resource getResource() {
89                 return resource;
90         }
91
92         public List<Resource> selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException {
93                 if (selector == null) {
94                         buildSelection(graph);
95                 }
96                 
97                 Collection<Resource> result = gather(graph, model);
98                 return result instanceof List ? (List<Resource>) result : new ArrayList<>(result);
99         }
100
101         private static Generator buildGenerator(ReadGraph graph, Resource resource) throws DatabaseException {
102                 if (!graph.isInstanceOf(resource, ES.Generator))
103                         throw new IllegalArgumentException("Resource " + resource + " is not a valid generator");
104                 
105                 if (graph.isInstanceOf(resource, ES.Generator_Model)) {
106                         return new ModelGenerator();
107                 }
108                 else if (graph.isInstanceOf(resource, ES.Generator_Diagram)) {
109                         return new DiagramGenerator(graph.getSingleObject(resource, ES.Generator_HasDiagram));
110                 }
111                 else if (graph.isInstanceOf(resource, ES.Generator_Explicit)) {
112                         return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement));
113                 }
114                 else {
115                         throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource)));
116                 }
117         }
118
119         private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException {
120                 if (!graph.isInstanceOf(resource, ES.Selector))
121                         throw new IllegalArgumentException("Resource " + resource + " is not a valid selector");
122                 
123                 Selector s;
124                 if (graph.isInstanceOf(resource, ES.Selector_All)) {
125                         s = new All();
126                 }
127                 else if (graph.isInstanceOf(resource, ES.PropertySelector)) {
128                         String propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName);
129                         Integer resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount);
130                         boolean isSmallest = graph.isInstanceOf(resource, ES.Selector_NLowest);
131                         s = new PropertySelector(isSmallest, propertyName, resultCount);
132                 }
133                 else {
134                         throw new IllegalArgumentException("Unknown selector type " + graph.getURI(graph.getSingleType(resource)));
135                 }
136                 return s;
137         }
138
139         private static Condition buildCondition(ReadGraph graph, Resource resource) throws DatabaseException {
140                 if (resource == null)
141                         return null;
142                 
143                 if (!graph.isInstanceOf(resource, ES.Condition))
144                         throw new IllegalArgumentException("Resource " + resource + " is not a valid condition");
145                 
146                 Condition cond;
147                 if (graph.isInstanceOf(resource, ES.PropertyCondition)) {
148                         String propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName);
149                         Double lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit);
150                         Double upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit);
151                         cond = new PropertyCondition(propertyName, lowerLimit, upperLimit);
152                 }
153                 else if (graph.isInstanceOf(resource, ES.RegionCondition)) {
154                         DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
155                         double[] region = graph.getRelatedValue(graph.getSingleObject(resource, ES.RegionCondition_HasRegion), DR.Region_area);
156                         cond = new RegionCondition(region);
157                 }
158                 else if (graph.isInstanceOf(resource, ES.RouteCondition)) {
159                         Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, graph.getSingleObject(resource, ES.RouteCondition_HasRoute)));
160                         cond = new RouteCondition(routePoints);
161                 }
162                 else if (graph.isInstanceOf(resource, ES.AggregateCondition)) {
163                         boolean isDisjunction = graph.isInstanceOf(resource, ES.Disjunction);
164                         Collection<Resource> conditionResources = graph.getObjects(resource, ES.HasSubcondition);
165                         List<Condition> conditions = new ArrayList<>(conditionResources.size());
166                         for (Resource c : conditionResources) {
167                                 conditions.add(buildCondition(graph, c));
168                         }
169                         Type type;
170                         if (graph.isInstanceOf(resource, ES.Conjunction))
171                                 type = AggregateCondition.Type.CONJUNCTION;
172                         else if (graph.isInstanceOf(resource, ES.Negation))
173                                 type = AggregateCondition.Type.NEGATION;
174                         else if (graph.isInstanceOf(resource, ES.Disjunction))
175                                 type = AggregateCondition.Type.DISJUNCTION;
176                         else
177                                 throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource)));
178                                 
179                         cond = new AggregateCondition(type, conditions);
180                 }
181                 else {
182                         throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
183                 }
184                 
185                 cond.isInverse = graph.hasStatement(resource, ES.Condition_IsInverse, resource);
186                 return cond;
187         }
188
189         private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
190                 if (graph.isInstanceOf(r, ES.Selection)) {
191                         String exp = "select " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
192                                      " from " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
193                         
194                         Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
195                         return cond != null ? exp + " where {" + buildExpression(graph, cond) + "}" : exp;
196                 }
197                 else if (graph.isInstanceOf(r, ES.Condition)) {
198                         if (graph.isInstanceOf(r, ES.PropertyCondition)) {
199                                 return buildPropertyConditionExpression(graph, r);
200                         }
201                         else if (graph.isInstanceOf(r, ES.RegionCondition)) {
202                                 Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion);
203                                 String name = graph.getRelatedValue(region, L0.HasLabel);
204                                 return "in region " + name;
205                         }
206                         else if (graph.isInstanceOf(r, ES.RouteCondition)) {
207                                 Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute);
208                                 String name = graph.getRelatedValue(route, L0.HasLabel);
209                                 return "in route " + name;
210                         }
211                         else if (graph.isInstanceOf(r, ES.AggregateCondition)) {
212                                 String op = graph.isInstanceOf(r, ES.Conjunction) ? " and " : " or ";
213                                 List<String> exps = new ArrayList<>();
214                                 Collection<Resource> objects = graph.getObjects(r, ES.HasSubcondition);
215                                 for (Resource c : objects) {
216                                         String exp = buildExpression(graph, c);
217                                         exps.add(objects.size() > 1 ? "{" + exp + "}" : exp);
218                                 }
219                                 String result = String.join(op, exps);
220                                 if (graph.isInstanceOf(r, ES.Negation))
221                                         result = "not {" + result + "}";
222                                 return result;
223                         }
224                         else {
225                                 throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
226                         }
227                 }
228                 else if (graph.isInstanceOf(r, ES.Selector)) {
229                         if (graph.isInstanceOf(r, ES.Selector_All)) {
230                                 return "all";
231                         }
232                         else if (graph.isInstanceOf(r, ES.PropertySelector)) {
233                                 String op;
234                                 if (graph.isInstanceOf(r, ES.Selector_NLowest))
235                                         op = "bottom";
236                                 else if (graph.isInstanceOf(r, ES.Selector_NHighest))
237                                         op = "top";
238                                 else
239                                         throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
240                                 
241                                 String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName);
242                                 Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount);
243                                 return op + " " + count + " of " + name;
244                         }
245                         else {
246                                 throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
247                         }
248                 }
249                 else if (graph.isInstanceOf(r, ES.Generator)) {
250                         if (graph.isInstanceOf(r, ES.Generator_Model)) {
251                                 return "model";
252                         }
253                         else if (graph.isInstanceOf(r, ES.Generator_Diagram)) {
254                                 return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\"";
255                         }
256                         else if (graph.isInstanceOf(r, ES.Generator_Explicit)) {
257                                 return "<list of " + graph.getObjects(r, ES.Generator_HasSelectedElement).size() + " elements>";
258                         }
259                         else {
260                                 throw new DatabaseException("Unsupported generator resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
261                         }
262                 }
263                 else {
264                         throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">");
265                 }
266         }
267
268         private static String buildPropertyConditionExpression(ReadGraph graph, Resource r) throws DatabaseException {
269                 String propertyName = graph.getRelatedValue(r, ES.PropertyCondition_HasPropertyName);
270                 Double lowerLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasLowerLimit);
271                 Double upperLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasUpperLimit);
272                 if (lowerLimit == null && upperLimit == null) {
273                         return "has property " + propertyName;
274                 }
275                 else {
276                         StringBuilder result = new StringBuilder();
277                         if (lowerLimit != null) {
278                                 result.append(lowerLimit);
279                                 result.append(" < ");
280                         }
281                         result.append(propertyName);
282                         if (upperLimit != null) {
283                                 result.append(" < ");
284                                 result.append(upperLimit);
285                         }
286                         return result.toString();
287                 }
288         }
289
290         private static Collection<Resource> elementsOfDiagram(ReadGraph graph, Resource diagram) throws ServiceException {
291                 Collection<Resource> elements = graph.getObjects(diagram, L0.ConsistsOf);
292                 Collection<Resource> result = new ArrayList<>();
293                 for (Resource r : elements)
294                         if (graph.isInstanceOf(r, DIA.Element))
295                                 result .add(r);
296                 return result;
297         }
298
299         private void buildSelection(ReadGraph graph) throws DatabaseException {
300                 Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector);
301                 Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator);
302                 Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition);
303                 
304                 this.selector = buildSelector(graph, selector);
305                 this.generator = buildGenerator(graph, generator);
306                 this.condition = buildCondition(graph, condition);
307         }
308
309         private Collection<Resource> filterElementsFrom(ReadGraph graph, Collection<Resource> elements) throws DatabaseException {
310                 if (condition == null) return elements;
311                 
312                 ArrayList<Resource> result = new ArrayList<>();
313                 for (Resource r : elements) {
314                         if (condition.match(graph, r))
315                                 result.add(r);
316                 }
317                 
318                 return result;
319         }
320         
321         private Collection<Resource> gather(ReadGraph graph, Resource model) throws DatabaseException {
322                 return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model)));
323         }
324         
325         static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException {
326                 Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);
327                 if (component != null) {
328                         Variable var = Variables.getVariable(graph, component);
329                         RVI realRvi = var.getRVI(graph);
330                         
331                         for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) {
332                                 for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) {
333                                         Variable v = realRvi.resolvePossible(graph, run);
334                                         if (v != null) {
335                                                 return v;
336                                         }
337                                 }
338                                 Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel);
339                                 if (configuration != null) {
340                                         Variable v = realRvi.resolvePossible(graph, configuration);
341                                         if (v != null) {
342                                                 return v;
343                                         }
344                                 }
345                         }
346                 }
347                 
348                 return null;
349         }
350         
351         static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) {
352                 try {
353                         Variable v = getVariableForElement(graph, element);
354                         if (v != null) {
355                                 Number value = v.getPossiblePropertyValue(graph, propertyName);
356                                 if (value != null)
357                                         return value.doubleValue();
358                         }
359                         
360                         // No property found - try possible mapped element property as well
361                         Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent);
362                         if (mappedElement != null)
363                                 return getPropertyValue(graph, mappedElement, propertyName);
364                 }
365                 catch (DatabaseException e) {
366                 }
367                 
368                 return null;
369         }
370         
371         // Generators
372         
373         static abstract class Generator {
374                 abstract Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException;
375         }
376         
377         static class ModelGenerator extends Generator {
378                 @Override
379                 Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
380                         Resource conf = graph.syncRequest(new Configuration(model));
381                         
382                         ArrayList<Resource> result = new ArrayList<>();
383                         
384                         // Iterate over diagrams
385                         for (Resource comp : graph.getObjects(conf, L0.ConsistsOf)) {
386                                 if (!graph.isInstanceOf(comp, STR.Composite)) continue;
387                                 
388                                 Resource diagram = graph.getPossibleObject(comp, MOD.CompositeToDiagram);
389                                 if (diagram != null) result.addAll(elementsOfDiagram(graph, diagram));
390                         }
391                         
392                         return result;
393                 }
394         }
395         
396         static class DiagramGenerator extends Generator {
397                 Resource diagram;
398                 
399                 public DiagramGenerator(Resource diagram) {
400                         this.diagram = diagram;
401                 }
402
403                 @Override
404                 Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
405                         return elementsOfDiagram(graph, diagram);
406                 }
407         }
408         
409         static class ExplicitGenerator extends Generator {
410                 public ExplicitGenerator(Collection<Resource> elements) {
411                         this.elements = elements;
412                 }
413
414                 Collection<Resource> elements;
415
416                 @Override
417                 Collection<Resource> generate(ReadGraph graph, Resource model) {
418                         return elements;
419                 }
420         }
421         
422         // Selectors
423         
424         static abstract class Selector {
425                 abstract Collection<Resource> select(ReadGraph graph, Collection<Resource> elements);
426         }
427         
428         static class All extends Selector {
429                 public All() {
430                 }
431
432                 @Override
433                 Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
434                         return elements;
435                 }
436         }
437         
438         static class PropertySelector extends Selector {
439                 public PropertySelector(boolean smallest, String propertyName, int resultCount) {
440                         this.smallest = smallest;
441                         this.propertyName = propertyName;
442                         this.resultCount = resultCount;
443                 }
444                 
445                 boolean smallest;
446                 String propertyName;
447                 int resultCount;
448                 
449                 @SuppressWarnings("unchecked")
450                 @Override
451                 Collection<Resource> select(ReadGraph graph, Collection<Resource> elements) {
452                         List<Resource> result = new ArrayList<>(elements);
453                         List<Tuple2> result2 = Lists.map(new FunctionImpl1<Resource, Tuple2>() {
454                                 @Override
455                                 public Tuple2 apply(Resource r) {
456                                         return new Tuple2(r, getPropertyValue(graph, r, propertyName));
457                                 }
458                         }, result);
459                         
460                         result2 = Lists.filter(new FunctionImpl1<Tuple2, Boolean>() {
461                                 @Override
462                                 public Boolean apply(Tuple2 t) {
463                                         return t.c1 != null;
464                                 }
465                         }, result2);
466                         
467                         result2.sort((t1, t2) -> smallest ? Double.compare((Double) t1.c1, (Double) t2.c1) : Double.compare((Double) t2.c1, (Double) t1.c1));
468                         
469                         result2 = result2.subList(0, resultCount);
470                         
471                         result = Lists.map(new FunctionImpl1<Tuple2, Resource>() {
472                                 @Override
473                                 public Resource apply(Tuple2 p0) {
474                                         return (Resource) p0.c0;
475                                 }
476                         }, result2);
477                         
478                         return result;
479                 }
480         }
481         
482         // Conditions
483         
484         static abstract class Condition {
485                 boolean isInverse;
486                 abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
487         }
488         
489         static class PropertyCondition extends Condition {
490                 public PropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
491                         super();
492                         this.propertyName = propertyName;
493                         this.lowerLimit = lowerLimit;
494                         this.upperLimit = upperLimit;
495                 }
496                 
497                 String propertyName;
498                 Double lowerLimit;
499                 Double upperLimit;
500                 
501                 @Override
502                 boolean match(ReadGraph graph, Resource r) {
503                         Double value = getPropertyValue(graph, r, propertyName);
504                         return value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit);
505                 }
506         }
507         
508         static class RegionCondition extends Condition {
509                 public RegionCondition(double[] region) {
510                         super();
511                         this.region = region;
512                         
513                         Path2D path = new Path2D.Double();
514                         double startX = region[0];
515                         double startY = region[1];
516                         path.moveTo(startX, startY);
517                         for (int i = 2; i < region.length; i+=2)
518                                 path.lineTo(region[i], region[i+1]);
519                         path.closePath();
520
521                         this.path = path;
522                 }
523
524                 double[] region;
525                 Path2D path;
526
527                 @Override
528                 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
529                         double[] transform = graph.getRelatedValue(r, DIA.HasTransform);
530                         double x = transform[4];
531                         double y = transform[5];
532                         return path.contains(x, y);
533                 } 
534         }
535         
536         static class RouteCondition extends Condition {
537                 public RouteCondition(Set<Resource> routePoints) {
538                         super();
539                         this.routePoints = routePoints;
540                 }
541
542                 Set<Resource> routePoints;
543
544                 @Override
545                 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
546                         return routePoints.contains(r);
547                 }
548         }
549         
550         static class DiagramCondition extends Condition {
551                 public DiagramCondition(Resource diagram) {
552                         super();
553                         this.diagram = diagram;
554                 }
555
556                 Resource diagram;
557
558                 @Override
559                 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
560                         return graph.getSingleObject(r, L0.PartOf).equals(diagram);
561                 }
562         }
563         
564         static class AggregateCondition extends Condition {
565                 static enum Type { DISJUNCTION, CONJUNCTION, NEGATION };
566                 
567                 public AggregateCondition(Type type, List<Condition> conditions) {
568                         super();
569                         this.type = type;
570                         this.conditions = conditions;
571                 }
572                 
573                 Type type;
574                 List<Condition> conditions;
575                 
576                 @Override
577                 boolean match(ReadGraph graph, Resource r) throws DatabaseException {
578                         switch (type) {
579                         case DISJUNCTION:
580                                 for (Condition c : conditions)
581                                         if (c.match(graph, r)) return true;
582                                 return false;
583                         case CONJUNCTION:
584                                 for (Condition c : conditions)
585                                         if (!c.match(graph, r)) return false;
586                                 return true;
587                         case NEGATION:
588                                 for (Condition c : conditions)
589                                         if (c.match(graph, r)) return false;
590                                 return true;
591                         default:
592                                 // Should not happen
593                                 throw new IllegalArgumentException("Unknown aggregate condition type " + type);
594                         }
595                 }
596         }
597         
598         public static class ElementSelectorQuery extends ResourceRead<ElementSelector> {
599                 public ElementSelectorQuery(Resource resource) {
600                         super(resource);
601                 }
602                 
603                 @Override
604                 public ElementSelector perform(ReadGraph graph) throws DatabaseException {
605                         return new ElementSelector(graph, resource);
606                 }
607         }
608 }