package org.simantics.structural2.variables; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; import org.simantics.db.common.request.BinaryRead; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.request.TransientUnaryRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.common.utils.NearestOwnerFinder; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.layer0.exception.MissingVariableException; import org.simantics.db.layer0.exception.MissingVariableValueException; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.service.CollectionSupport; import org.simantics.db.service.QueryControl; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.Functions; import org.simantics.structural2.Functions.InterfaceResolution; import org.simantics.structural2.queries.ConnectionSet; import org.simantics.structural2.utils.StructuralUtils; import org.simantics.structural2.utils.StructuralUtils.StructuralComponentClass; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; public class ConnectionBrowser { /** * Finds the components connected by the connection. Also connections * in * * @param graph * @param connection A connection whose related modules are searched. * @param configuration A variable that represents the composite where the connection belongs to. * @return A map whose keys are components and they are mapped to * related variables. */ public static Collection findConnectedComponents( ReadGraph graph, Resource connection, Variable configuration) throws DatabaseException { // Create state ArrayList result = new ArrayList(); THashSet visitedConnections = new THashSet(); // Do actual work findConnectedComponents(graph, connection, configuration, result, visitedConnections); return result; } private static void findConnectedComponents( ReadGraph graph, Resource connection, Variable configuration, ArrayList result, THashSet visitedConnections) throws DatabaseException { if(visitedConnections.add(connection)) { StructuralResource2 STR = StructuralResource2.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); // Browse related components for(Statement stat : graph.getStatements(connection, STR.Connects)) { Resource component = stat.getObject(); Resource relation = graph.getInverse(stat.getPredicate()); //System.out.println(NameUtils.getSafeName(graph, component) + "." + NameUtils.getSafeName(graph, relation)); Resource boundConnection = graph.getPossibleObject(relation, STR.IsBoundBy); Resource type = StructuralUtils.getPossibleComponentType(graph, configuration, component); Resource def = type != null ? graph.getPossibleObject(type, STR.IsDefinedBy) : null; if(boundConnection != null && def != null) { // The connection point is bound in component type Variable newContext = configuration.browsePossible(graph, component); Resource newComposite = getCompositeOfConnection(graph, boundConnection); if(newContext != null && newComposite != null) { newContext = browse(graph, def, newContext, newComposite); if (newContext != null) findConnectedComponents(graph, boundConnection, newContext, result, visitedConnections); } } else { //System.out.println("added result"); // A primitive connection point Variable context = configuration.browsePossible(graph, component); if (context != null) result.add(new ResourceWithContext(component, context)); } } // Browse over connection joins for(Resource join : graph.getObjects(connection, STR.IsJoinedBy)) for(Resource otherConnection : graph.getObjects(join, STR.Joins)) if(!connection.equals(otherConnection)) { Resource sourceComposite = getCompositeOfConnection(graph, connection); Resource targetComposite = getCompositeOfConnection(graph, otherConnection); if (sourceComposite != null && targetComposite != null) { Variable sibling = browseSibling(graph, sourceComposite, configuration, targetComposite); if (sibling != null) findConnectedComponents(graph, otherConnection, sibling, result, visitedConnections); } } // Browse to parents try { for(Resource relation : graph.getObjects(connection, STR.Binds)) { Resource composite = getCompositeOfConnection(graph, connection); if (composite == null) continue; Variable curConfiguration = configuration; while(!graph.hasStatement(composite, STR.Defines)) { composite = graph.getSingleObject(composite, L0.PartOf); curConfiguration = curConfiguration.getParent(graph); } Variable parent = curConfiguration.getParent(graph); Resource component = curConfiguration.getRepresents(graph); for(Resource c : graph.getObjects(component, relation)) findConnectedComponents(graph, c, parent, result, visitedConnections); } } catch(NoSingleResultException e) { } catch(MissingVariableException e) { } catch(MissingVariableValueException e) { } } } public static Collection drill(ReadGraph graph, VariableConnectionPointDescriptor pair) throws DatabaseException { Collection interfaceDescription = pair.getInterfaceDescription(graph); if(interfaceDescription != null && interfaceDescription.size() > 0) { Variable cp = pair.getVariable(graph); Variable context = cp.getParent(graph); String cpName = cp.getName(graph); Collection result = new ArrayList(); for(InterfaceResolution r : interfaceDescription) { if(r.interfaceName.equals(cpName)) { String path = Functions.resolveInterfacePath(graph, context, r.componentName, r.connectionPoint); result.add(new BrowseConnectionDescriptor(context, path)); } } if(result.isEmpty()) return null; return result; } else { return Collections.singleton(pair); } } public static class JoinConnections extends ResourceRead> { public JoinConnections(Resource join) { super(join); } @Override public Collection perform(ReadGraph graph) throws DatabaseException { ConnectionSet cs = new ConnectionSet(graph); cs.addJoin(graph, resource); return cs.getConnections(); } } public static final class VariableChildren extends TransientUnaryRead> { public VariableChildren(ReadGraph graph, Variable variable) throws DatabaseException { super(graph, variable); } public VariableChildren(ReadGraph graph, QueryControl qc, Variable variable) throws DatabaseException { super(graph, qc, variable); } @Override public Map perform(ReadGraph graph, Variable parameter) throws DatabaseException { CollectionSupport cs = graph.getService(CollectionSupport.class); Map result = cs.createMap(Variable.class); for(Variable child : parameter.getChildren(graph)) { Resource represents = child.getPossibleRepresents(graph); if(represents != null) result.put(represents, child); } return result; } } static Variable resolve(ReadGraph graph, Variable base, Resource component) throws DatabaseException { Map map = graph.syncRequest(new VariableChildren(graph, base), TransientCacheAsyncListener.>instance()); Variable result = map.get(component); if(result != null) return result; else { Layer0 L0 = Layer0.getInstance(graph); Resource parent = graph.getPossibleObject(component, L0.PartOf); if(parent == null) return null; Variable v = resolve(graph, base, parent); if (v == null) return null; map = graph.syncRequest(new VariableChildren(graph, v), TransientCacheAsyncListener.>instance()); return map.get(component); } } public static class ConnectionComponentsWithAncestor extends TransientUnaryRead> { final private List result; public ConnectionComponentsWithAncestor(ReadGraph graph, Resource conn) throws DatabaseException { this(graph, conn, null); } public ConnectionComponentsWithAncestor(ReadGraph graph, QueryControl qc, Resource conn, List result) throws DatabaseException { super(graph, qc, conn); this.result = result; } public ConnectionComponentsWithAncestor(ReadGraph graph, Resource conn, List result) throws DatabaseException { super(graph, conn); this.result = result; } private ConnectionSet connSet(ReadGraph graph, Resource r ) throws DatabaseException { ConnectionSet cs = new ConnectionSet(graph); cs.addConnection(graph, r); return cs; } @Override public List perform(ReadGraph graph, Resource resource) throws DatabaseException { if(result != null) return result; Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); CollectionSupport colls = graph.getService(CollectionSupport.class); THashSet ancestorGenerators = new THashSet(); Set parts = colls.createSet(); ConnectionSet cs = connSet(graph, resource); for(Resource connRes : cs.getConnections()) { for(Statement stm : graph.getStatements(connRes, STR.Connects)) { Resource component = stm.getObject(); Resource parent = graph.getPossibleObject(component, L0.PartOf); if(parent != null && !graph.isInstanceOf(component, ModelingResources.getInstance(graph).ReferenceElement)) ancestorGenerators.add(parent); } parts.add(connRes); } for (Resource join : cs.getJoins()) { parts.add(join); for (Resource composite : graph.getObjects(join, STR.JoinsComposite)) ancestorGenerators.add(composite); } Resource ancestor = ancestorGenerators.size() == 1 ? ancestorGenerators.iterator().next() : NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, ancestorGenerators); List result = colls.createList(); result.add(ancestor); result.addAll(colls.asSortedList(parts)); if(parameter != WITH_PARENT) { for(int i=1;i>instance()); } } return result; } } public static Collection climb(ReadGraph graph, Variable child, Resource cp, String subPath_) throws DatabaseException { boolean isStructural = false; Variable curConfiguration = child.getParent(graph); { Collection interfaceDescription = Functions.computeInterfacePaths(graph, curConfiguration); if(interfaceDescription != null) { isStructural = interfaceDescription != Functions.BUILTIN_STRUCTURAL_CPS; if(interfaceDescription.size() > 0) { if(subPath_ == null) { String childName = child.getName(graph); for(InterfaceResolution r : interfaceDescription) { if(r.componentName.equals(childName) && r.connectionPoint.equals(cp)) { Variable pConn = curConfiguration.getPossibleProperty(graph, r.interfaceName); if(pConn != null) { Resource cp2 = pConn.getPossiblePredicateResource(graph); Collection res = climb(graph, curConfiguration, cp2, null); if(res != null) return res; } return Collections.emptyList(); } } } else { throw new UnsupportedOperationException(""); } } } } if(child instanceof StandardProceduralChildVariable) { Variable conn = child.getPossibleProperty(graph, cp); FixedConnection fc = (FixedConnection)conn.getValue(graph); Set result = new THashSet(1+fc.size()); result.add(new ComponentConnectionDescriptor(child, cp)); fc.addConnectionDescriptors(graph, curConfiguration, result); return result; } else { Resource res = cp; Resource represents = child.getRepresents(graph); if(isStructural) { Collection conns = graph.getObjects(represents, res); HashSet result = new HashSet(); for(Resource c : conns) { List rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, c), TransientCacheAsyncListener.>instance()); result.addAll(graph.syncRequest(ConnectionVariables.forStructural(graph, curConfiguration, rs))); } return result; } else { Resource connection = graph.getPossibleObject(represents, res); if(connection != null) { List rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, connection), TransientCacheAsyncListener.>instance()); return graph.syncRequest(ConnectionVariables.forConfiguration(graph, curConfiguration, rs)); } else { Collection conns = graph.getObjects(represents, res); HashSet result = new HashSet(); for(Resource c : conns) { List rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, c), TransientCacheAsyncListener.>instance()); result.addAll(graph.syncRequest(ConnectionVariables.forConfiguration(graph, curConfiguration, rs))); } return result; } } } } public static void reportDescriptor(ReadGraph graph, VariableConnectionPointDescriptor d) throws DatabaseException { if(d instanceof ActualConnectionDescriptor) { ActualConnectionDescriptor d2 = (ActualConnectionDescriptor)d; System.err.println("--ActualConnectionPointDescriptor2"); System.err.println("---root: " + d2.root.getURI(graph)); System.err.println("---component: " + graph.getPossibleURI(d2.component)); System.err.println("---type: " + graph.getPossibleURI(d2.componentType)); System.err.println("---cp: " + graph.getPossibleURI(d2.cp)); System.err.println("---var: " + d2.getVariable(graph).getURI(graph)); } } public static class ConnectionVariables extends BinaryRead, Collection> { private ConnectionVariables(Variable parameter1, List parameter2) { super(parameter1, parameter2); } public static ConnectionVariables forConfiguration(ReadGraph graph, Variable configuration, List rs) throws DatabaseException { return new ConnectionVariables(parent(graph, configuration, rs.get(0)), rs); } public static ConnectionVariables forStructural(ReadGraph graph, Variable configuration, List rs) throws DatabaseException { return new ConnectionVariables(configuration, rs); } /** * Finds the parent variable of configuration that * represents ancestor. * * @param graph * @param configuration * @param ancestor * @return * @throws DatabaseException if no parent was found that represents ancestor */ private static Variable parent(ReadGraph graph, Variable configuration, Resource ancestor) throws DatabaseException { Variable v = configuration; Resource represents = v.getRepresents(graph); while(!represents.equals(ancestor)) { v = v.getParent(graph); if (v == null) { throw new DatabaseException( "parent representing ancestor not found for variable, configuration=" + safeURI(graph, configuration) + ", ancestor=" + NameUtils.getURIOrSafeNameInternal(graph, ancestor)); } represents = v.getRepresents(graph); } return v; } @Override public Collection perform(ReadGraph graph) throws DatabaseException { if(parameter == null) return Collections.emptyList(); Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); ArrayList result = null; for(int i=1;i(); String componentName = graph.getRelatedValue(component, L0.HasName, Bindings.STRING); Variable possibleChild = parameter.getPossibleChild(graph, componentName); if(possibleChild != null) { Resource type = possibleChild.getPossibleType(graph, STR.Component); if(type != null) { result.add(new ActualConnectionDescriptor(parameter, component, possibleChild.getType(graph), connectionPoint)); } else { throw new DatabaseException("Child does not have a structural type: " + possibleChild.getURI(graph)); } } else { Resource type = graph.getPossibleType(component, STR.Component); if(type != null) { result.add(new ActualConnectionDescriptor(parameter, component, type, connectionPoint)); } else { throw new DatabaseException("Child with name " + componentName + " does not have a structural type: " + parameter.getURI(graph)); } } } } if(result == null) return Collections.emptyList(); return result; } } static class IsLeafType extends ResourceRead { protected IsLeafType(Resource type) { super(type); } @Override public Boolean perform(ReadGraph graph) throws DatabaseException { StructuralComponentClass clazz = StructuralComponentClass.get(graph, resource); return StructuralComponentClass.PRIMITIVE.equals(clazz); } } static class ChildMapOfVariable extends VariableRead> { public ChildMapOfVariable(Variable variable) { super(variable); } @Override public Map perform(ReadGraph graph) throws DatabaseException { HashMap result = new HashMap(); for(Variable child : variable.getChildren(graph)) { Resource represents = child.getPossibleRepresents(graph); if(represents != null) result.put(represents, child); } return result; } } /** * Given a root composite, related variable and some other component inside the composite, * finds the related variable for that component. */ public static Variable browse(ReadGraph graph, Resource root, Variable rootContext, Resource target) throws DatabaseException { if(target.equals(root)) return rootContext; else { Layer0 L0 = Layer0.getInstance(graph); String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING); Resource parent = graph.getPossibleObject(target, L0.PartOf); if(name == null || parent == null) return null; Variable parentVariable = browse(graph, root, rootContext, parent); if(parentVariable == null) return null; return parentVariable.getPossibleChild(graph, name); } } /** * Finds a variable whose location related to sourceContext is the same as * between target and source. In other words, the method solves {@code targetContext} * in the following equations: *
     *     URI(source)        = resourceURIBase + sourceSuffix
     *     URI(sourceContext) = variableURIBase + sourceSuffix
     *     URI(target)        = resourceURIBase + targetSuffix
     *     URI(targetContext) = variableURIBase + targetSuffix
     * 
*/ public static Variable browseSibling(ReadGraph graph, Resource source, Variable sourceContext, Resource target) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); THashMap sourceMap = new THashMap(); while(source != null && sourceContext != null) { sourceMap.put(source, sourceContext); source = graph.getPossibleObject(source, L0.PartOf); sourceContext = sourceContext.getParent(graph); } return browseSibling(graph, sourceMap, target); } private static Variable browseSibling(ReadGraph graph, THashMap sourceMap, Resource target) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Variable result = sourceMap.get(target); if(result != null) return result; String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING); Resource parent = graph.getPossibleObject(target, L0.PartOf); if(name == null || parent == null) return null; Variable parentVariable = browseSibling(graph, sourceMap, parent); if(parentVariable == null) return null; return parentVariable.getPossibleChild(graph, name); } /** * Returns the composite where the connection given as a parameter resides. */ public static Resource getCompositeOfConnection(ReadGraph graph, Resource connection) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); // First from connected components for(Resource component : graph.getObjects(connection, STR.Connects)) for(Resource composite : graph.getObjects(component, L0.PartOf)) return composite; // It could be that the connection is only supported by joins (input flag -> output flag) - use diagram info TODO!! Resource connToDiagramConn = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/ConnectionToDiagramConnection"); if(connToDiagramConn != null) { Resource diagramConnection = graph.getPossibleObject(connection, connToDiagramConn); if(diagramConnection != null) { Resource diagram = graph.getPossibleObject(diagramConnection, L0.PartOf); if(diagram != null) { Resource diagramToComposite = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/DiagramToComposite"); if(diagramToComposite != null) { return graph.getPossibleObject(diagram, diagramToComposite); } } } } return null; } static class Flatten extends BinaryRead> { public Flatten(Variable parameter1, Resource parameter2) { super(parameter1, parameter2); } @Override public Collection perform(ReadGraph graph) throws DatabaseException { return doFlatten(graph, parameter, parameter2, null); } } public static Collection flatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException { if(relationType == null) return graph.syncRequest(new Flatten(child, cp)); return doFlatten(graph, child, cp, relationType); } public static Collection doFlatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException { Set result = null; Set needDrill = null; Collection climbed = climb(graph, child, cp, null); for(VariableConnectionPointDescriptor desc : climbed) { if(!desc.isLeaf(graph)) { if(needDrill == null) needDrill = new THashSet(climbed.size()); needDrill.add(desc); } else { if(relationType != null) { if(!filterByRelationType(graph, desc, relationType)) continue; } if(result == null) result = new THashSet(climbed.size()); result.add(desc); } } if(needDrill != null) { /* * There were some descriptors that require drill */ for(VariableConnectionPointDescriptor top : needDrill) { Collection drilled = drill(graph, top); if(drilled != null) { for(VariableConnectionPointDescriptor drill : drilled) { if(relationType != null) { if(!filterByRelationType(graph, drill, relationType)) continue; } if(result == null) result = new THashSet(climbed.size()); result.add(drill); } } } } if (result != null) { return result; } else { return Collections.emptySet(); } } private static boolean filterByRelationType(ReadGraph graph, VariableConnectionPointDescriptor desc, Resource relationType) throws DatabaseException { Resource predicateResource = desc.getConnectionPointResource(graph); return predicateResource != null && graph.isInstanceOf(predicateResource, relationType); } private static String safeURI(ReadGraph graph, Variable v) { if (v == null) return "null variable"; try { return v.getURI(graph); } catch (DatabaseException e) { return v.toString(); } } }