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