X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.district.selection%2Fsrc%2Forg%2Fsimantics%2Fdistrict%2Fselection%2FElementSelector.java;h=6de06ff690ea23a03304e119e412309301e581c1;hb=refs%2Fchanges%2F42%2F4242%2F1;hp=9de46638032cc7baf9ce0b952beabe4cd2c4e56c;hpb=77fb2992f4eab37919e21af7f24a70a0a6ecc336;p=simantics%2Fdistrict.git diff --git a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java index 9de46638..6de06ff6 100644 --- a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java +++ b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java @@ -3,45 +3,61 @@ 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.district.selection.ElementSelector.AggregateCondition.Type; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; -import org.simantics.scl.runtime.Lists; -import org.simantics.scl.runtime.function.FunctionImpl1; -import org.simantics.scl.runtime.tuple.Tuple2; 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; - Integer count; + Resource resource; Generator generator; Selector selector; Condition condition; + private float[] color; + static Logger LOG = LoggerFactory.getLogger(ElementSelector.class); static ElementSelectionResource ES; @@ -49,6 +65,9 @@ public class ElementSelector { 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(); @@ -58,59 +77,208 @@ public class ElementSelector { STR = StructuralResource2.getInstance(graph); MOD = ModelingResources.getInstance(graph); DIA = DiagramResource.getInstance(graph); + DN = DistrictNetworkResource.getInstance(graph); this.resource = resource; - this.count = -1; try { this.name = graph.getRelatedValue(resource, L0.HasLabel); - this.expression = buildExpression(graph, resource); + this.expression = getExpression(graph, resource); + + float[] color = graph.getPossibleRelatedValue(resource, ES.Selection_HasHighlightColor); + this.color = color; } catch (DatabaseException e) { LOG.error("Error reading element selector", e); throw e; } } - public static ElementSelector getSelector(ReadGraph graph, Resource resource) throws DatabaseException { + /** + * 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; } - public List selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException { + /** + * 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; + } + + /** + * Get the selection highlight color as a four element BGRA array. + */ + public float[] getColor() { + return color; + } + + /** + * + * @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); } - Collection result = gather(graph, model); - return result instanceof List ? (List) result : new ArrayList<>(result); + 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)) { + if (graph.isInstanceOf(resource, ES.Generator_Model)) return new ModelGenerator(); - } - else if (graph.isInstanceOf(resource, ES.Generator_Diagram)) { + 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)) { + else if (graph.isInstanceOf(resource, ES.Generator_Explicit)) return new ExplicitGenerator(graph.getObjects(resource, ES.Generator_HasSelectedElement)); - } - else { + else throw new IllegalArgumentException("Unknown generator type " + graph.getURI(graph.getSingleType(resource))); - } } private static Selector buildSelector(ReadGraph graph, Resource resource) throws DatabaseException { @@ -118,18 +286,16 @@ public class ElementSelector { throw new IllegalArgumentException("Resource " + resource + " is not a valid selector"); Selector s; - if (graph.isInstanceOf(resource, ES.Selector_All)) { + if (graph.isInstanceOf(resource, ES.Selector_All)) s = new All(); - } - else if (graph.isInstanceOf(resource, ES.PropertySelector)) { - String propertyName = graph.getRelatedValue(resource, ES.PropertySelector_HasSelectionPropertyName); - Integer resultCount = graph.getRelatedValue(resource, ES.PropertySelector_HasResultCount); - boolean isSmallest = graph.isInstanceOf(resource, ES.Selector_NLowest); - s = new PropertySelector(isSmallest, propertyName, resultCount); - } - else { + 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; } @@ -141,150 +307,189 @@ public class ElementSelector { throw new IllegalArgumentException("Resource " + resource + " is not a valid condition"); Condition cond; - if (graph.isInstanceOf(resource, ES.PropertyCondition)) { - String propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName); - Double lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit); - Double upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit); - cond = new PropertyCondition(propertyName, lowerLimit, upperLimit); + 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(resource, ES.RegionCondition)) { - DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph); - double[] region = graph.getRelatedValue(graph.getSingleObject(resource, ES.RegionCondition_HasRegion), DR.Region_area); - cond = new RegionCondition(region); + else if (graph.isInstanceOf(r, ES.Generator_Diagram)) { + return "diagram \"" + graph.getRelatedValue(graph.getSingleObject(r, ES.Generator_HasDiagram), L0.HasName) + "\""; } - else if (graph.isInstanceOf(resource, ES.RouteCondition)) { - Set routePoints = new HashSet<>(ListUtils.toList(graph, graph.getSingleObject(resource, ES.RouteCondition_HasRoute))); - cond = new RouteCondition(routePoints); + else if (graph.isInstanceOf(r, ES.Generator_Explicit)) { + return ""; } - else if (graph.isInstanceOf(resource, ES.AggregateCondition)) { - boolean isDisjunction = graph.isInstanceOf(resource, ES.Disjunction); - Collection conditionResources = graph.getObjects(resource, ES.HasSubcondition); - List conditions = new ArrayList<>(conditionResources.size()); - for (Resource c : conditionResources) { - conditions.add(buildCondition(graph, c)); - } - Type type; - if (graph.isInstanceOf(resource, ES.Conjunction)) - type = AggregateCondition.Type.CONJUNCTION; - else if (graph.isInstanceOf(resource, ES.Negation)) - type = AggregateCondition.Type.NEGATION; - else if (graph.isInstanceOf(resource, ES.Disjunction)) - type = AggregateCondition.Type.DISJUNCTION; - else - throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource))); - - cond = new AggregateCondition(type, conditions); + 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 IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource))); + throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); } - cond.isInverse = graph.hasStatement(resource, ES.Condition_IsInverse, resource); - return cond; + 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 buildExpression(ReadGraph graph, Resource r) throws DatabaseException { - if (graph.isInstanceOf(r, ES.Selection)) { - String exp = "select " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + - " from " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator)); - - Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition); - return cond != null ? exp + " where {" + buildExpression(graph, cond) + "}" : 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.Condition)) { - if (graph.isInstanceOf(r, ES.PropertyCondition)) { - return buildPropertyConditionExpression(graph, r); - } - else if (graph.isInstanceOf(r, ES.RegionCondition)) { - Resource region = graph.getSingleObject(r, ES.RegionCondition_HasRegion); - String name = graph.getRelatedValue(region, L0.HasLabel); - return "in region " + name; - } - else if (graph.isInstanceOf(r, ES.RouteCondition)) { - Resource route = graph.getSingleObject(r, ES.RouteCondition_HasRoute); - String name = graph.getRelatedValue(route, L0.HasLabel); - return "in route " + name; - } - else if (graph.isInstanceOf(r, ES.AggregateCondition)) { - 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 = buildExpression(graph, c); - exps.add(objects.size() > 1 ? "{" + exp + "}" : exp); - } - String result = String.join(op, exps); - if (graph.isInstanceOf(r, ES.Negation)) - result = "not {" + result + "}"; - return result; - } - else { - throw new DatabaseException("Unsupported condition resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); - } + else if (graph.isInstanceOf(r, ES.RegionCondition)) { + result = buildRegionConditionExpression(graph, r); } - else if (graph.isInstanceOf(r, ES.Selector)) { - if (graph.isInstanceOf(r, ES.Selector_All)) { - return "all"; - } - else if (graph.isInstanceOf(r, ES.PropertySelector)) { - String op; - if (graph.isInstanceOf(r, ES.Selector_NLowest)) - op = "bottom"; - else if (graph.isInstanceOf(r, ES.Selector_NHighest)) - op = "top"; - else - throw new DatabaseException("Unsupported property selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); - - String name = graph.getRelatedValue(r, ES.PropertySelector_HasSelectionPropertyName); - Integer count = graph.getRelatedValue(r, ES.PropertySelector_HasResultCount); - return op + " " + count + " of " + name; - } - else { - throw new DatabaseException("Unsupported selector resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); - } + else if (graph.isInstanceOf(r, ES.RouteCondition)) { + result = buildRouteConditionExpression(graph, r); } - else if (graph.isInstanceOf(r, ES.Generator)) { - 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)) + ">"); - } + else if (graph.isInstanceOf(r, ES.AggregateCondition)) { + // This handles isInverse internally + return buildAggregateConditionExpression(graph, r, isInverse); } else { - throw new DatabaseException("Unsupported resource type <" + graph.getURI(graph.getSingleType(r)) + ">"); + 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 (lowerLimit == null && upperLimit == null) { - return "has property " + propertyName; - } - else { + 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(" < "); + result.append(" \u2264 "); } result.append(propertyName); - if (upperLimit != null) { - result.append(" < "); - result.append(upperLimit); - } + result.append(" \u2264 "); + result.append(upperLimit); return result.toString(); } } - private static Collection elementsOfDiagram(ReadGraph graph, Resource diagram) throws ServiceException { + 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) @@ -293,16 +498,6 @@ public class ElementSelector { return result; } - private 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); - } - private Collection filterElementsFrom(ReadGraph graph, Collection elements) throws DatabaseException { if (condition == null) return elements; @@ -315,78 +510,48 @@ public class ElementSelector { return result; } - private Collection gather(ReadGraph graph, Resource model) throws DatabaseException { + private SelectionResult gather(ReadGraph graph, Resource model) throws DatabaseException { return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model))); } - 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; - } - - 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(); - } - } - catch (DatabaseException e) { - } - - return null; - } - // Generators - static abstract class Generator { + public static abstract class Generator { abstract Collection generate(ReadGraph graph, Resource model) throws DatabaseException; } - static class ModelGenerator extends Generator { + public static class ModelGenerator extends Generator { @Override Collection generate(ReadGraph graph, Resource model) throws DatabaseException { Resource conf = graph.syncRequest(new Configuration(model)); - ArrayList result = new ArrayList<>(); + 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) result.addAll(elementsOfDiagram(graph, diagram)); + 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 result; + return new ArrayList<>(fromMapped.values()); } } - static class DiagramGenerator extends Generator { - Resource diagram; + public static class DiagramGenerator extends Generator { + public Resource diagram; public DiagramGenerator(Resource diagram) { this.diagram = diagram; @@ -398,12 +563,12 @@ public class ElementSelector { } } - static class ExplicitGenerator extends Generator { + public static class ExplicitGenerator extends Generator { public ExplicitGenerator(Collection elements) { this.elements = elements; } - Collection elements; + public Collection elements; @Override Collection generate(ReadGraph graph, Resource model) { @@ -413,160 +578,351 @@ public class ElementSelector { // Selectors - static abstract class Selector { - abstract Collection select(ReadGraph graph, Collection elements); + 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; + } } - static class All extends Selector { + public static class All extends Selector { public All() { } @Override - Collection select(ReadGraph graph, Collection elements) { - return elements; + SelectionResult select(ReadGraph graph, Collection elements) { + Collection selected = filterElements(graph, elements); + return new SelectionResult(selected, 0, 0); } } - static class PropertySelector extends Selector { + public static class PropertySelector extends Selector { public PropertySelector(boolean smallest, String propertyName, int resultCount) { this.smallest = smallest; this.propertyName = propertyName; this.resultCount = resultCount; } - boolean smallest; - String propertyName; - int 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; - @SuppressWarnings("unchecked") @Override - Collection select(ReadGraph graph, Collection elements) { - List result = new ArrayList<>(elements); - List result2 = Lists.map(new FunctionImpl1() { - @Override - public Tuple2 apply(Resource r) { - return new Tuple2(r, getPropertyValue(graph, r, propertyName)); - } - }, result); + SelectionResult select(ReadGraph graph, Collection elements) { + elements = filterElements(graph, elements); - result2 = Lists.filter(new FunctionImpl1() { - @Override - public Boolean apply(Tuple2 t) { - return t.c1 != null; - } - }, result2); + // Select sorting direction + Comparator> comparator = smallest ? + (p1, p2) -> Double.compare(p1.second, p2.second) : + (p1, p2) -> Double.compare(p2.second, p1.second); - result2.sort((t1, t2) -> smallest ? Double.compare((Double) t1.c1, (Double) t2.c1) : Double.compare((Double) t2.c1, (Double) t1.c1)); + // 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()); - result2 = result2.subList(0, resultCount); + // 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; + } - result = Lists.map(new FunctionImpl1() { - @Override - public Resource apply(Tuple2 p0) { - return (Resource) p0.c0; - } - }, result2); + // 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; + } - return result; + // 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 - static abstract class Condition { - boolean isInverse; - abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException; + 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); + ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); + 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 { + ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); + + assert(resource != null); + if (isInverse) + graph.claim(resource, ES.Condition_IsInverse, resource); + else + graph.deny(resource, ES.Condition_IsInverse, resource); + return resource; + } } - static class PropertyCondition extends Condition { - public PropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) { - super(); + public static class PropertyCondition extends Condition { + public PropertyCondition(ReadGraph graph, Resource r) throws DatabaseException { + super(graph, r); + + ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); + 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; } - String propertyName; - Double lowerLimit; - Double upperLimit; + public String propertyName; + public Double lowerLimit; + public Double upperLimit; @Override - boolean match(ReadGraph graph, Resource r) { + public boolean match(ReadGraph graph, Resource r) { Double value = getPropertyValue(graph, r, propertyName); - return value != null && (lowerLimit == null || value >= lowerLimit) && (upperLimit == null || value <= upperLimit); + 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; } } - static class RegionCondition extends Condition { - public RegionCondition(double[] region) { - super(); + 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); + ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); + 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(); - 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(); + 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(); + } - this.path = path; + return path; } + public Resource regionResource; double[] region; Path2D path; @Override - boolean match(ReadGraph graph, Resource r) throws DatabaseException { + 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); - } + return path.contains(x, y) ^ isInverse; + } + + @Override + public Resource update(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + ElementSelectionResource ES = ElementSelectionResource.getInstance(graph); + DiagramRegionsResource DR = DiagramRegionsResource.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 + this.region = regionResource != null ? graph.getRelatedValue(regionResource, DR.Region_area, Bindings.DOUBLE_ARRAY) : null; + this.path = createPathForRegion(region); + + return resource; + } } - static class RouteCondition extends Condition { - public RouteCondition(Set routePoints) { - super(); + public static class RouteCondition extends Condition { + public RouteCondition(Resource r, Resource routeResource, Set routePoints) { + super(r); this.routePoints = routePoints; + this.routeResource = routeResource; } - Set routePoints; - - @Override - boolean match(ReadGraph graph, Resource r) throws DatabaseException { - return routePoints.contains(r); + public RouteCondition(ReadGraph graph, Resource r) throws DatabaseException { + super(graph, r); + this.routeResource = graph.getPossibleObject(resource, ES.RouteCondition_HasRoute); + this.routePoints = getRoutePoints(graph, routeResource); } - } - - static class DiagramCondition extends Condition { - public DiagramCondition(Resource diagram) { - super(); - this.diagram = diagram; + + public static Set getRoutePoints(ReadGraph graph, Resource routeResource) throws DatabaseException { + return routeResource != null ? + new HashSet<>(ListUtils.toList(graph, routeResource)) : + Collections.emptySet(); } - Resource diagram; + public Resource routeResource; + Set routePoints; @Override - boolean match(ReadGraph graph, Resource r) throws DatabaseException { - return graph.getSingleObject(r, L0.PartOf).equals(diagram); + 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; } } - static class AggregateCondition extends Condition { - static enum Type { DISJUNCTION, CONJUNCTION, NEGATION }; + public static class AggregateCondition extends Condition { + public static enum Type { DISJUNCTION, CONJUNCTION, NEGATION }; - public AggregateCondition(Type type, List conditions) { - super(); + public AggregateCondition(Resource r, Type type, List conditions) { + super(r); this.type = type; this.conditions = conditions; } - Type type; - List 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 - boolean match(ReadGraph graph, Resource r) throws DatabaseException { + 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) @@ -585,6 +941,41 @@ public class ElementSelector { throw new IllegalArgumentException("Unknown aggregate condition type " + type); } } + + @Override + public Resource update(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + ElementSelectionResource ES = ElementSelectionResource.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 { @@ -597,4 +988,26 @@ public class ElementSelector { 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); + } + } }