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