package org.simantics.district.selection; import java.awt.geom.Path2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.simantics.Simantics; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.utils.ListUtils; import org.simantics.db.exception.AssumptionException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.DoesNotContainValueException; import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.exception.ServiceException; import org.simantics.db.exception.ValidationException; import org.simantics.db.layer0.QueryIndexUtils; import org.simantics.db.layer0.request.ActiveModels; import org.simantics.db.layer0.request.ActiveRuns; import org.simantics.db.layer0.request.Configuration; import org.simantics.db.layer0.request.PossibleActiveModel; import org.simantics.db.layer0.variable.RVI; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.request.Read; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.district.region.ontology.DiagramRegionsResource; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ElementSelector { String name; String expression; Resource resource; Generator generator; Selector selector; Condition condition; static Logger LOG = LoggerFactory.getLogger(ElementSelector.class); static ElementSelectionResource ES; static Layer0 L0; static StructuralResource2 STR; static ModelingResources MOD; static DiagramResource DIA; static DistrictNetworkResource DN; private static Logger LOGGER = LoggerFactory.getLogger(ElementSelector.class); ElementSelector(ReadGraph graph, Resource resource) throws DatabaseException { super(); L0 = Layer0.getInstance(graph); ES = ElementSelectionResource.getInstance(graph); STR = StructuralResource2.getInstance(graph); MOD = ModelingResources.getInstance(graph); DIA = DiagramResource.getInstance(graph); DN = DistrictNetworkResource.getInstance(graph); this.resource = resource; try { this.name = graph.getRelatedValue(resource, L0.HasLabel); this.expression = getExpression(graph, resource); } catch (DatabaseException e) { LOG.error("Error reading element selector", e); throw e; } } /** * Instantiate an element selector object for an ES.Selection resource. */ public static ElementSelector getSelector(RequestProcessor graph, Resource resource) throws DatabaseException { return graph.syncRequest(new ElementSelectorQuery(resource)); } /** * Get an SQL-like textual description of an ES.Selection resource. */ public static String getExpression(RequestProcessor graph, Resource resource) throws DatabaseException { return graph.syncRequest(new SelectionExpressionRequest(resource)); } /** * Get a Java object representation of an ES.Condition resource. */ public static Condition getCondition(RequestProcessor graph, Resource condition) throws DatabaseException { return graph.syncRequest(new SelectionConditionRequest(condition)); } /** * Get the name of the element selector. */ public String getName() { return name; } /** * Get a textual SQL-like description of the element selector. */ public String getExpression() { return expression; } /** * Get the resource that this selector represents. */ public Resource getResource() { return resource; } /** * Get the generator component. Use {@link #buildSelection(ReadGraph)} to make it available first. */ public Generator getGenerator() { return generator; } /** * Get the selector component. Use {@link #buildSelection(ReadGraph)} to make it available first. */ public Selector getSelector() { return selector; } /** * Get the condition component. Use {@link #buildSelection(ReadGraph)} to make it available first. */ public Condition getCondition() { return condition; } /** * * @param graph * @throws DatabaseException */ public void buildSelection(ReadGraph graph) throws DatabaseException { Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector); Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator); Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition); this.selector = buildSelector(graph, selector); this.generator = buildGenerator(graph, generator); this.condition = buildCondition(graph, condition); } public static Map findDiagrams() { try { return Simantics.getSession().syncRequest(new Read>() { @Override public Map perform(ReadGraph graph) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); Map result = new HashMap<>(); Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource())); List composites = QueryIndexUtils.searchByType(graph, model, STR.Composite); for (Resource r : composites) { // Get diagram Resource diagram = graph.getPossibleObject(r, MOD.CompositeToDiagram); if (diagram == null || !graph.isInstanceOf(diagram, DN.Diagram)) continue; // Filter out user component diagrams Resource parent = graph.getPossibleObject(r, L0.PartOf); if (parent == null || graph.isInheritedFrom(parent, STR.Component)) continue; result.put(r, graph.getRelatedValue(r, L0.HasName)); } return result; } }); } catch (DatabaseException e) { LOGGER.error("Query for model diagrams failed", e); return Collections.emptyMap(); } } public static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException { Resource component = graph.getPossibleObject(element, MOD.ElementToComponent); if (component != null) { Variable var = Variables.getVariable(graph, component); RVI realRvi = var.getRVI(graph); for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) { for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) { Variable v = realRvi.resolvePossible(graph, run); if (v != null) { return v; } } Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel); if (configuration != null) { Variable v = realRvi.resolvePossible(graph, configuration); if (v != null) { return v; } } } } return null; } public static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) { try { Variable v = getVariableForElement(graph, element); if (v != null) { Number value = v.getPossiblePropertyValue(graph, propertyName); if (value != null) return value.doubleValue(); } // No property found - try possible mapped element property as well Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent); if (mappedElement != null) return getPropertyValue(graph, mappedElement, propertyName); } catch (DatabaseException e) { } return null; } public SelectionResult selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException { if (selector == null) { buildSelection(graph); } return gather(graph, model); } private static Generator buildGenerator(ReadGraph graph, Resource resource) throws DatabaseException { if (!graph.isInstanceOf(resource, ES.Generator)) throw new IllegalArgumentException("Resource " + resource + " is not a valid generator"); if (graph.isInstanceOf(resource, ES.Generator_Model)) return new ModelGenerator(); else if (graph.isInstanceOf(resource, ES.Generator_Diagram)) return new DiagramGenerator(graph.getSingleObject(resource, ES.Generator_HasDiagram)); else if (graph.isInstanceOf(resource, ES.Generator_Explicit)) return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement)); else throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource))); } private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException { if (!graph.isInstanceOf(resource, ES.Selector)) throw new IllegalArgumentException("Resource " + resource + " is not a valid selector"); Selector s; if (graph.isInstanceOf(resource, ES.Selector_All)) s = new All(); else if (graph.isInstanceOf(resource, ES.PropertySelector)) s = new PropertySelector(graph, resource); else throw new IllegalArgumentException("Unknown selector type " + graph.getURI(graph.getSingleType(resource))); Resource mapping = graph.getPossibleObject(resource, ES.Selector_HasMapping); s.componentType = mapping; return s; } private static Condition buildCondition(ReadGraph graph, Resource resource) throws DatabaseException { if (resource == null) return null; if (!graph.isInstanceOf(resource, ES.Condition)) throw new IllegalArgumentException("Resource " + resource + " is not a valid condition"); Condition cond; if (graph.isInstanceOf(resource, ES.PropertyCondition)) cond = new PropertyCondition(graph, resource); else if (graph.isInstanceOf(resource, ES.RegionCondition)) cond = new RegionCondition(graph, resource); else if (graph.isInstanceOf(resource, ES.RouteCondition)) cond = new RouteCondition(graph, resource); else if (graph.isInstanceOf(resource, ES.AggregateCondition)) cond = new AggregateCondition(graph, resource); else throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource))); return cond; } private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException { if (graph.isInstanceOf(r, ES.Selection)) return buildSelectionExpression(graph, r); else if (graph.isInstanceOf(r, ES.Condition)) return buildConditionExpression(graph, r); else if (graph.isInstanceOf(r, ES.Selector)) return buildSelectorExpression(graph, r); else if (graph.isInstanceOf(r, ES.Generator)) return buildGeneratorExpression(graph, r); else throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); } private static String buildSelectionExpression(ReadGraph graph, Resource r) throws DatabaseException, NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException { String exp = "select " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + " from " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator)); Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition); return cond != null ? exp + " where {" + getExpression(graph, cond) + "}" : exp; } private static String buildGeneratorExpression(ReadGraph graph, Resource r) throws ServiceException, NoSingleResultException, DoesNotContainValueException, ManyObjectsForFunctionalRelationException, DatabaseException, AssumptionException, ValidationException { if (graph.isInstanceOf(r, ES.Generator_Model)) { return "model"; } else if (graph.isInstanceOf(r, ES.Generator_Diagram)) { return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\""; } else if (graph.isInstanceOf(r, ES.Generator_Explicit)) { return ""; } else { throw new DatabaseException("Unsupported generator resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); } } private static String buildSelectorExpression(ReadGraph graph, Resource r) throws ServiceException, NoSingleResultException, DoesNotContainValueException, DatabaseException, AssumptionException, ValidationException, ManyObjectsForFunctionalRelationException { String exp; if (graph.isInstanceOf(r, ES.Selector_All)) { exp = "all"; } else if (graph.isInstanceOf(r, ES.PropertySelector)) { Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount); exp = count.toString(); } else { throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); } Resource mapping = graph.getPossibleObject(r, ES.Selector_HasMapping); if (mapping != null) { String name = graph.getRelatedValue2(mapping, L0.HasName); exp = exp + " " + name; } else { exp = exp + " elements"; } if (graph.isInstanceOf(r, ES.PropertySelector)) { String op; if (graph.isInstanceOf(r, ES.Selector_NLowest)) op = "lowest"; else if (graph.isInstanceOf(r, ES.Selector_NHighest)) op = "highest"; else throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName); exp = exp + " with " + op + " " + name; } return exp; } private static String buildConditionExpression(ReadGraph graph, Resource r) throws ServiceException, DatabaseException, NoSingleResultException, ManyObjectsForFunctionalRelationException, DoesNotContainValueException, AssumptionException, ValidationException { String result; boolean isInverse = graph.hasStatement(r, ES.Condition_IsInverse, r); if (graph.isInstanceOf(r, ES.PropertyCondition)) { result = buildPropertyConditionExpression(graph, r); } else if (graph.isInstanceOf(r, ES.RegionCondition)) { result = buildRegionConditionExpression(graph, r); } else if (graph.isInstanceOf(r, ES.RouteCondition)) { result = buildRouteConditionExpression(graph, r); } else if (graph.isInstanceOf(r, ES.AggregateCondition)) { // This handles isInverse internally return buildAggregateConditionExpression(graph, r, isInverse); } else { throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); } if (isInverse) result = "not {" + result + "}"; return result; } private static String buildAggregateConditionExpression(ReadGraph graph, Resource r, boolean isInverse) throws ServiceException, DatabaseException { String result; String op = graph.isInstanceOf(r, ES.Conjunction) ? " and " : " or "; List exps = new ArrayList<>(); Collection objects = graph.getObjects(r, ES.HasSubcondition); for (Resource c : objects) { String exp = getExpression(graph, c); exps.add(objects.size() > 1 ? "{" + exp + "}" : exp); } result = String.join(op, exps); if (graph.isInstanceOf(r, ES.Negation) ^ isInverse) result = "not {" + result + "}"; return result; } private static String buildRouteConditionExpression(ReadGraph graph, Resource r) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException, DoesNotContainValueException { String result; Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute); String name = graph.getRelatedValue(route, L0.HasLabel); result = "in route " + name; return result; } private static String buildRegionConditionExpression(ReadGraph graph, Resource r) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException, DoesNotContainValueException { String result; Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion); String name = graph.getRelatedValue(region, L0.HasLabel); result = "in region " + name; return result; } private static String buildPropertyConditionExpression(ReadGraph graph, Resource r) throws DatabaseException { String propertyName = graph.getRelatedValue(r, ES.PropertyCondition_HasPropertyName); Double lowerLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasLowerLimit); Double upperLimit = graph.getPossibleRelatedValue(r, ES.PropertyCondition_HasUpperLimit); if (upperLimit == null) { if (lowerLimit == null) { return "has property " + propertyName; } else { return propertyName + " \u2265 " + lowerLimit; } } else { StringBuilder result = new StringBuilder(); if (lowerLimit != null) { result.append(lowerLimit); result.append(" \u2264 "); } result.append(propertyName); result.append(" \u2264 "); result.append(upperLimit); return result.toString(); } } private static Collection elementsOfDiagram(ReadGraph graph, Resource diagram) throws DatabaseException { if (graph.isInstanceOf(diagram, STR.Composite)) { // Resource is a composite - get diagram return elementsOfDiagram(graph, graph.getSingleObject(diagram, MOD.CompositeToDiagram)); } Collection elements = graph.getObjects(diagram, L0.ConsistsOf); Collection result = new ArrayList<>(); for (Resource r : elements) if (graph.isInstanceOf(r, DIA.Element)) result .add(r); return result; } private Collection filterElementsFrom(ReadGraph graph, Collection elements) throws DatabaseException { if (condition == null) return elements; ArrayList result = new ArrayList<>(); for (Resource r : elements) { if (condition.match(graph, r)) result.add(r); } return result; } private SelectionResult gather(ReadGraph graph, Resource model) throws DatabaseException { return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model))); } // Generators public static abstract class Generator { abstract Collection generate(ReadGraph graph, Resource model) throws DatabaseException; } public static class ModelGenerator extends Generator { @Override Collection generate(ReadGraph graph, Resource model) throws DatabaseException { Resource conf = graph.syncRequest(new Configuration(model)); HashMap fromMapped = new HashMap(); // Iterate over diagrams // Each model element is represented by the corresponding mapping element, if present for (Resource comp : graph.getObjects(conf, L0.ConsistsOf)) { if (!graph.isInstanceOf(comp, STR.Composite)) continue; Resource diagram = graph.getPossibleObject(comp, MOD.CompositeToDiagram); if (diagram != null) { for (Resource elem : elementsOfDiagram(graph, diagram)) { Resource mapped = graph.getPossibleObject(elem, DN.MappedComponent); if (mapped != null) { fromMapped.put(mapped, elem); } else if (!fromMapped.containsKey(elem)) { fromMapped.put(elem, elem); } } } } return new ArrayList<>(fromMapped.values()); } } public static class DiagramGenerator extends Generator { public Resource diagram; public DiagramGenerator(Resource diagram) { this.diagram = diagram; } @Override Collection generate(ReadGraph graph, Resource model) throws DatabaseException { return elementsOfDiagram(graph, diagram); } } public static class ExplicitGenerator extends Generator { public ExplicitGenerator(Collection elements) { this.elements = elements; } public Collection elements; @Override Collection generate(ReadGraph graph, Resource model) { return elements; } } // Selectors public static class SelectionResult { public final Collection elements; public final int tailCount; public final int tailSize; public SelectionResult(Collection elements, int tailCount, int tailSize) { this.elements = elements; this.tailCount = tailCount; this.tailSize = tailSize; } } public static abstract class Selector { public Resource componentType = null; abstract SelectionResult select(ReadGraph graph, Collection elements); Collection filterElements(ReadGraph graph, Collection elements) { if (componentType == null) return elements; Collection selected = new HashSet<>(elements.size()); for (Resource r : elements) { try { if (graph.hasStatement(r, DN.HasMapping, componentType)) selected.add(r); } catch (DatabaseException e) { // Just leave it out of the result } } return selected; } } public static class All extends Selector { public All() { } @Override SelectionResult select(ReadGraph graph, Collection elements) { Collection selected = filterElements(graph, elements); return new SelectionResult(selected, 0, 0); } } public static class PropertySelector extends Selector { public PropertySelector(boolean smallest, String propertyName, int resultCount) { this.smallest = smallest; this.propertyName = propertyName; this.resultCount = resultCount; } public PropertySelector(ReadGraph graph, Resource resource) throws DatabaseException { this.propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName); this.resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount); this.smallest = graph.isInstanceOf(resource, ES.Selector_NLowest); } public boolean smallest; public String propertyName; public int resultCount; @Override SelectionResult select(ReadGraph graph, Collection elements) { elements = filterElements(graph, elements); // Select sorting direction Comparator> comparator = smallest ? (p1, p2) -> Double.compare(p1.second, p2.second) : (p1, p2) -> Double.compare(p1.second, p2.second); // Get association list to property values List> result2 = elements.stream() .map(r -> Pair.make(r, getPropertyValue(graph, r, propertyName))) .filter(t -> t.second != null) .sorted(comparator) .collect(Collectors.toList()); int count = Math.min(resultCount, result2.size()); // Count number of equal values at the end of the list int tailCount = 0; double tailValue = count > 0 ? result2.get(count-1).second : 0.0; for (int i = count-1; i >= 0; i--) { if (result2.get(i).second == tailValue) tailCount++; else break; } // Count number of elements with value equal to the end of the list int tailSize = tailCount; for (int i = count; i < result2.size(); i++) { if (result2.get(i).second == tailValue) tailSize++; else break; } // Take first n items if (count < result2.size()) { result2 = result2.subList(0, resultCount); } // Map to list or resources List selection = result2.stream().map(p -> p.first).collect(Collectors.toList()); return new SelectionResult(selection, tailCount, tailSize); } } // Conditions public static abstract class Condition { public Resource resource; public boolean isInverse; Condition(Resource r) { resource = r; isInverse = false; } Condition(ReadGraph graph, Resource r) throws DatabaseException { this(r); isInverse = graph.hasStatement(r, ES.Condition_IsInverse, r); } public abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException; public Resource update(WriteGraph graph) throws DatabaseException { assert(resource != null); if (isInverse) graph.claim(resource, ES.Condition_IsInverse, resource); else graph.deny(resource, ES.Condition_IsInverse, resource); return resource; } } public static class PropertyCondition extends Condition { public PropertyCondition(ReadGraph graph, Resource r) throws DatabaseException { super(graph, r); this.propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName); this.lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit); this.upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit); } public PropertyCondition(Resource r, String propertyName, Double lowerLimit, Double upperLimit) { super(r); this.propertyName = propertyName; this.lowerLimit = lowerLimit; this.upperLimit = upperLimit; } public String propertyName; public Double lowerLimit; public Double upperLimit; @Override public boolean match(ReadGraph graph, Resource r) { Double value = getPropertyValue(graph, r, propertyName); boolean result = value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit); return result ^ isInverse; } @Override public Resource update(WriteGraph graph) throws DatabaseException { ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); if (resource == null) { resource = graph.newResource(); graph.claim(resource, L0.InstanceOf, ES.PropertyCondition); } super.update(graph); graph.claimLiteral(resource, ES.PropertyCondition_HasPropertyName, propertyName); if (lowerLimit != null) graph.claimLiteral(resource, ES.PropertyCondition_HasLowerLimit, L0.Double, lowerLimit); else graph.deny(resource, ES.PropertyCondition_HasLowerLimit); if (upperLimit != null) graph.claimLiteral(resource, ES.PropertyCondition_HasUpperLimit, L0.Double, upperLimit); else graph.deny(resource, ES.PropertyCondition_HasUpperLimit); return resource; } } public static class RegionCondition extends Condition { public RegionCondition(Resource r, Resource regionResource, double[] region) { super(r); this.region = region; this.path = createPathForRegion(region); this.regionResource = regionResource; } public RegionCondition(ReadGraph graph, Resource r) throws DatabaseException { super(graph, r); DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph); this.regionResource = graph.getPossibleObject(resource, ES.RegionCondition_HasRegion); this.region = regionResource != null ? graph.getRelatedValue(regionResource, DR.Region_area) : null; this.path = createPathForRegion(region); } public static Path2D createPathForRegion(double[] region) { Path2D path = new Path2D.Double(); if (region != null) { double startX = region[0]; double startY = region[1]; path.moveTo(startX, startY); for (int i = 2; i < region.length; i+=2) path.lineTo(region[i], region[i+1]); path.closePath(); } return path; } public Resource regionResource; double[] region; Path2D path; @Override public boolean match(ReadGraph graph, Resource r) throws DatabaseException { double[] transform = graph.getRelatedValue(r, DIA.HasTransform); double x = transform[4]; double y = transform[5]; return path.contains(x, y) ^ isInverse; } @Override public Resource update(WriteGraph graph) throws DatabaseException { ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); if (resource == null) { resource = graph.newResource(); graph.claim(resource, L0.InstanceOf, ES.RegionCondition); } super.update(graph); graph.claim(resource, ES.RegionCondition_HasRegion, regionResource); // Re-read region data to match DB DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph); this.region = regionResource != null ? graph.getRelatedValue(regionResource, DR.Region_area, Bindings.DOUBLE_ARRAY) : null; this.path = createPathForRegion(region); return resource; } } public static class RouteCondition extends Condition { public RouteCondition(Resource r, Resource routeResource, Set routePoints) { super(r); this.routePoints = routePoints; this.routeResource = routeResource; } public RouteCondition(ReadGraph graph, Resource r) throws DatabaseException { super(graph, r); this.routeResource = graph.getPossibleObject(resource, ES.RouteCondition_HasRoute); this.routePoints = getRoutePoints(graph, routeResource); } public static Set getRoutePoints(ReadGraph graph, Resource routeResource) throws DatabaseException { return routeResource != null ? new HashSet<>(ListUtils.toList(graph, routeResource)) : Collections.emptySet(); } public Resource routeResource; Set routePoints; @Override public boolean match(ReadGraph graph, Resource r) throws DatabaseException { return routePoints.contains(r) ^ isInverse; } @Override public Resource update(WriteGraph graph) throws DatabaseException { ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); if (resource == null) { resource = graph.newResource(); graph.claim(resource, L0.InstanceOf, ES.RouteCondition); } super.update(graph); if (routeResource != null) graph.claim(resource, ES.RouteCondition_HasRoute, routeResource); this.routePoints = getRoutePoints(graph, routeResource); return resource; } } public static class AggregateCondition extends Condition { public static enum Type { DISJUNCTION, CONJUNCTION, NEGATION }; public AggregateCondition(Resource r, Type type, List conditions) { super(r); this.type = type; this.conditions = conditions; } public AggregateCondition(ReadGraph graph, Resource r) throws DatabaseException { super(graph, r); Collection conditionResources = graph.getObjects(resource, ES.HasSubcondition); conditions = new ArrayList<>(conditionResources.size()); for (Resource c : conditionResources) { conditions.add(buildCondition(graph, c)); } if (graph.isInstanceOf(resource, ES.Conjunction)) this.type = AggregateCondition.Type.CONJUNCTION; else if (graph.isInstanceOf(resource, ES.Negation)) this.type = AggregateCondition.Type.NEGATION; else if (graph.isInstanceOf(resource, ES.Disjunction)) this.type = AggregateCondition.Type.DISJUNCTION; else throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource))); } public Type type; public List conditions; @Override public boolean match(ReadGraph graph, Resource r) throws DatabaseException { return doMatch(graph, r) ^ isInverse; } private boolean doMatch(ReadGraph graph, Resource r) throws DatabaseException { switch (type) { case DISJUNCTION: for (Condition c : conditions) if (c.match(graph, r)) return true; return false; case CONJUNCTION: for (Condition c : conditions) if (!c.match(graph, r)) return false; return true; case NEGATION: for (Condition c : conditions) if (c.match(graph, r)) return false; return true; default: // Should not happen throw new IllegalArgumentException("Unknown aggregate condition type " + type); } } @Override public Resource update(WriteGraph graph) throws DatabaseException { ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); if (resource == null) { resource = graph.newResource(); } else { graph.deny(resource, L0.InstanceOf); graph.deny(resource, ES.HasSubcondition); } Resource type; switch (this.type) { case CONJUNCTION: type = ES.Conjunction; break; case DISJUNCTION: type = ES.Disjunction; break; case NEGATION: type = ES.Negation; break; default: throw new IllegalStateException("Unknown condition type " + this.type); } graph.claim(resource, L0.InstanceOf, type); super.update(graph); for (Condition c : conditions) { graph.claim(resource, ES.HasSubcondition, c.update(graph)); } return resource; } } public static class ElementSelectorQuery extends ResourceRead { public ElementSelectorQuery(Resource resource) { super(resource); } @Override public ElementSelector perform(ReadGraph graph) throws DatabaseException { return new ElementSelector(graph, resource); } } public final static class SelectionExpressionRequest extends ResourceRead { public SelectionExpressionRequest(Resource condition) { super(condition); } @Override public String perform(ReadGraph graph) throws DatabaseException { return ElementSelector.buildExpression(graph, resource); } } public static final class SelectionConditionRequest extends ResourceRead { public SelectionConditionRequest(Resource resource) { super(resource); } @Override public Condition perform(ReadGraph graph) throws DatabaseException { return buildCondition(graph, resource); } } }