]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural2/src/org/simantics/structural2/variables/ConnectionBrowser.java
New implementation NearestOwnerFinder of CommonDBUtils.getNearestOwner
[simantics/platform.git] / bundles / org.simantics.structural2 / src / org / simantics / structural2 / variables / ConnectionBrowser.java
1 package org.simantics.structural2.variables;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import org.simantics.databoard.Bindings;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.Resource;
15 import org.simantics.db.Statement;
16 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
17 import org.simantics.db.common.request.BinaryRead;
18 import org.simantics.db.common.request.ResourceRead;
19 import org.simantics.db.common.request.TransientUnaryRead;
20 import org.simantics.db.common.utils.CommonDBUtils;
21 import org.simantics.db.common.utils.NameUtils;
22 import org.simantics.db.common.utils.NearestOwnerFinder;
23 import org.simantics.db.exception.DatabaseException;
24 import org.simantics.db.exception.NoSingleResultException;
25 import org.simantics.db.layer0.exception.MissingVariableException;
26 import org.simantics.db.layer0.exception.MissingVariableValueException;
27 import org.simantics.db.layer0.request.VariableRead;
28 import org.simantics.db.layer0.variable.Variable;
29 import org.simantics.db.service.CollectionSupport;
30 import org.simantics.db.service.QueryControl;
31 import org.simantics.layer0.Layer0;
32 import org.simantics.modeling.ModelingResources;
33 import org.simantics.structural.stubs.StructuralResource2;
34 import org.simantics.structural2.Functions;
35 import org.simantics.structural2.Functions.InterfaceResolution;
36 import org.simantics.structural2.queries.ConnectionSet;
37 import org.simantics.structural2.utils.StructuralUtils;
38 import org.simantics.structural2.utils.StructuralUtils.StructuralComponentClass;
39 import org.simantics.structural2.variables.StandardProceduralChildVariable.FixedConnection;
40 import org.simantics.utils.datastructures.Pair;
41
42 import gnu.trove.map.hash.THashMap;
43 import gnu.trove.set.hash.THashSet;
44
45 public class ConnectionBrowser {
46
47     /**
48      * Finds the components connected by the connection. Also connections
49      * in 
50      * 
51      * @param graph 
52      * @param connection A connection whose related modules are searched.
53      * @param configuration A variable that represents the composite where the connection belongs to.
54      * @return A map whose keys are components and they are mapped to 
55      *         related variables.
56      */
57     public static Collection<ResourceWithContext> findConnectedComponents(
58             ReadGraph graph, Resource connection, Variable configuration) 
59             throws DatabaseException {
60         // Create state
61         ArrayList<ResourceWithContext> result = 
62             new ArrayList<ResourceWithContext>();
63         THashSet<Resource> visitedConnections = new THashSet<Resource>();
64
65         // Do actual work
66         findConnectedComponents(graph, connection, configuration, result,
67                 visitedConnections);
68         return result;
69     }
70
71     private static void findConnectedComponents(
72             ReadGraph graph, Resource connection, Variable configuration,
73             ArrayList<ResourceWithContext> result,
74             THashSet<Resource> visitedConnections) throws DatabaseException {
75          if(visitedConnections.add(connection)) {
76              StructuralResource2 STR = StructuralResource2.getInstance(graph);
77              Layer0 L0 = Layer0.getInstance(graph);
78
79              // Browse related components
80              for(Statement stat : graph.getStatements(connection, STR.Connects)) {
81                  Resource component = stat.getObject();
82                  Resource relation = graph.getInverse(stat.getPredicate());
83                  //System.out.println(NameUtils.getSafeName(graph, component) + "." + NameUtils.getSafeName(graph, relation));
84                  Resource boundConnection = graph.getPossibleObject(relation, STR.IsBoundBy);
85                  Resource type = StructuralUtils.getPossibleComponentType(graph, configuration, component);
86                  Resource def = type != null ? graph.getPossibleObject(type, STR.IsDefinedBy) : null;
87                  if(boundConnection != null && def != null) {
88                      // The connection point is bound in component type
89                      Variable newContext = configuration.browsePossible(graph, component);
90                      Resource newComposite = getCompositeOfConnection(graph, boundConnection);
91                      if(newContext != null && newComposite != null) {
92                          newContext = browse(graph, def, newContext, newComposite);
93                          if (newContext != null)
94                              findConnectedComponents(graph, boundConnection,
95                                      newContext,
96                                      result, visitedConnections);
97                      }
98                  }
99                  else {
100                      //System.out.println("added result");
101                      // A primitive connection point
102                      Variable context = configuration.browsePossible(graph, component);
103                      if (context != null)
104                          result.add(new ResourceWithContext(component, context)); 
105                  }
106              }
107
108              // Browse over connection joins
109              for(Resource join : graph.getObjects(connection, STR.IsJoinedBy))
110                  for(Resource otherConnection : graph.getObjects(join, STR.Joins))
111                      if(!connection.equals(otherConnection)) {
112                          Resource sourceComposite = getCompositeOfConnection(graph, connection);
113                          Resource targetComposite = getCompositeOfConnection(graph, otherConnection);
114                          if (sourceComposite != null && targetComposite != null) {
115                              Variable sibling = browseSibling(graph, 
116                                      sourceComposite,
117                                      configuration,
118                                      targetComposite);
119                              if (sibling != null)
120                                  findConnectedComponents(graph, otherConnection,
121                                          sibling, result, visitedConnections);
122                          }
123                      }
124
125              // Browse to parents
126              try {
127                  for(Resource relation : graph.getObjects(connection, STR.Binds)) {
128                      Resource composite = getCompositeOfConnection(graph, connection);
129                      if (composite == null)
130                          continue;
131
132                      Variable curConfiguration = configuration;
133                      while(!graph.hasStatement(composite, STR.Defines)) {
134                          composite = graph.getSingleObject(composite, L0.PartOf);
135                          curConfiguration = curConfiguration.getParent(graph);
136                      }
137                      Variable parent = curConfiguration.getParent(graph);
138                      Resource component = curConfiguration.getRepresents(graph);
139                      for(Resource c : graph.getObjects(component, relation))
140                          findConnectedComponents(graph, c, 
141                                  parent, result, visitedConnections);
142                  }
143              } catch(NoSingleResultException e) {
144              } catch(MissingVariableException e) {
145              } catch(MissingVariableValueException e) {
146              }
147          }
148     }
149
150     public static Collection<VariableConnectionPointDescriptor> drill(ReadGraph graph, VariableConnectionPointDescriptor pair) throws DatabaseException {
151
152         Collection<InterfaceResolution> interfaceDescription = pair.getInterfaceDescription(graph);
153         if(interfaceDescription != null && interfaceDescription.size() > 0) {
154
155                 Variable cp = pair.getVariable(graph);
156                 Variable context = cp.getParent(graph);
157                 String cpName = cp.getName(graph);
158
159                 Collection<VariableConnectionPointDescriptor> result = new ArrayList<VariableConnectionPointDescriptor>();
160                 for(InterfaceResolution r : interfaceDescription) {
161                         if(r.interfaceName.equals(cpName)) {
162                                 String path = Functions.resolveInterfacePath(graph, context, r.componentName, r.connectionPoint);
163                                 result.add(new BrowseConnectionDescriptor(context, path));
164                         }
165                 }
166                 
167                 if(result.isEmpty()) return null;
168                 
169                 return result;
170                 
171         } else {
172                 return Collections.singleton(pair);
173         }
174
175     }
176     
177     public static class JoinConnections extends ResourceRead<Collection<Resource>> {
178
179                 public JoinConnections(Resource join) {
180                         super(join);
181                 }
182
183                 @Override
184                 public Collection<Resource> perform(ReadGraph graph) throws DatabaseException {
185                 ConnectionSet cs = new ConnectionSet(graph);
186                 cs.addJoin(graph, resource);
187                 return cs.getConnections();
188                 }
189         
190     }
191
192     
193     public static final class VariableChildren extends TransientUnaryRead<Variable, Map<Resource,Variable>> {
194
195                 public VariableChildren(ReadGraph graph, Variable variable) throws DatabaseException {
196                         super(graph, variable);
197                 }
198
199                 public VariableChildren(ReadGraph graph, QueryControl qc, Variable variable) throws DatabaseException {
200                         super(graph, qc, variable);
201                 }
202
203                 @Override
204                 public Map<Resource, Variable> perform(ReadGraph graph, Variable parameter) throws DatabaseException {
205                         CollectionSupport cs = graph.getService(CollectionSupport.class);
206                         Map<Resource,Variable> result = cs.createMap(Variable.class);
207                         for(Variable child : parameter.getChildren(graph)) {
208                                 Resource represents = child.getPossibleRepresents(graph);
209                                 if(represents != null) result.put(represents, child);
210                         }
211                         return result;
212                 }
213         
214     }
215     
216     static Variable resolve(ReadGraph graph, Variable base, Resource component) throws DatabaseException {
217         Map<Resource,Variable> map = graph.syncRequest(new VariableChildren(graph, base), TransientCacheAsyncListener.<Map<Resource,Variable>>instance());
218         Variable result = map.get(component);
219         if(result != null) return result;
220         else {
221                 Layer0 L0 = Layer0.getInstance(graph);
222                 Resource parent = graph.getPossibleObject(component, L0.PartOf);
223                 if(parent == null) return null;
224                 Variable v = resolve(graph, base, parent);
225                 if (v == null) return null;
226                 map = graph.syncRequest(new VariableChildren(graph, v), TransientCacheAsyncListener.<Map<Resource,Variable>>instance());
227                 return map.get(component);
228         }
229     }
230     
231     public static class ConnectionComponentsWithAncestor extends TransientUnaryRead<Resource, List<Resource>> {
232
233         final private List<Resource> result;
234
235                 public ConnectionComponentsWithAncestor(ReadGraph graph, Resource conn) throws DatabaseException {
236                         this(graph, conn, null);
237                 }
238
239         public ConnectionComponentsWithAncestor(ReadGraph graph, QueryControl qc, Resource conn, List<Resource> result) throws DatabaseException {
240                         super(graph, qc, conn);
241                         this.result = result;
242         }
243
244                 public ConnectionComponentsWithAncestor(ReadGraph graph, Resource conn, List<Resource> result) throws DatabaseException {
245                         super(graph, conn);
246                         this.result = result;
247                 }
248
249                 private ConnectionSet connSet(ReadGraph graph, Resource r ) throws DatabaseException {
250                         ConnectionSet cs = new ConnectionSet(graph);
251                         cs.addConnection(graph, r);
252                         return cs;
253                 }
254                 
255                 @Override
256                 public List<Resource> perform(ReadGraph graph, Resource resource) throws DatabaseException {
257                         
258                         if(result != null) return result;
259                         
260                         Layer0 L0 = Layer0.getInstance(graph);
261                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
262                         CollectionSupport colls = graph.getService(CollectionSupport.class);
263                         THashSet<Resource> ancestorGenerators = new THashSet<Resource>();
264                         Set<Resource> parts = colls.createSet();
265                         ConnectionSet cs = connSet(graph, resource);
266             for(Resource connRes : cs.getConnections()) {
267                 for(Statement stm : graph.getStatements(connRes, STR.Connects)) {
268                         Resource component = stm.getObject();
269                         Resource parent = graph.getPossibleObject(component, L0.PartOf);
270                         if(parent != null && !graph.isInstanceOf(component, ModelingResources.getInstance(graph).ReferenceElement))
271                                 ancestorGenerators.add(parent);
272                 }
273                 parts.add(connRes);
274             }
275             for (Resource join : cs.getJoins()) {
276                 parts.add(join);
277                 for (Resource composite : graph.getObjects(join, STR.JoinsComposite))
278                         ancestorGenerators.add(composite);
279             }
280             Resource ancestor = ancestorGenerators.size() == 1 ? ancestorGenerators.iterator().next() : NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, ancestorGenerators);
281             
282             List<Resource> result = colls.createList();
283             result.add(ancestor);
284             result.addAll(colls.asSortedList(parts));
285             
286             if(parameter != WITH_PARENT) {
287                 for(int i=1;i<result.size();i++) {
288                         Resource r = result.get(i);
289                         // Cache 'em all
290                         if(!r.equals(resource))
291                                 graph.syncRequest(new ConnectionComponentsWithAncestor(graph, r, result), TransientCacheAsyncListener.<List<Resource>>instance());
292                     }
293             }
294             
295             return result;
296             
297                 }
298         
299     }
300     
301     public static Collection<VariableConnectionPointDescriptor> climb(ReadGraph graph, Variable child, Resource cp, String subPath_) throws DatabaseException {
302         
303         boolean isStructural = false;
304
305         Variable curConfiguration = child.getParent(graph);
306
307         {
308
309                         Collection<InterfaceResolution> interfaceDescription = Functions.computeInterfacePaths(graph, curConfiguration);
310                 if(interfaceDescription != null) {
311                         isStructural = interfaceDescription != Functions.BUILTIN_STRUCTURAL_CPS;
312                         if(interfaceDescription.size() > 0) {
313                                 
314                                 if(subPath_ == null) {
315                                         
316                                         String childName = child.getName(graph);
317                                         for(InterfaceResolution r : interfaceDescription) {
318                                                 if(r.componentName.equals(childName) && r.connectionPoint.equals(cp)) {
319                                                         Variable pConn = curConfiguration.getPossibleProperty(graph, r.interfaceName);
320                                                         if(pConn != null) {
321                                                                 Resource cp2 = pConn.getPossiblePredicateResource(graph);
322                                                                 Collection<VariableConnectionPointDescriptor> res = climb(graph, curConfiguration, cp2, null);
323                                                                 if(res != null) return res;
324                                                         }
325                                                         return Collections.emptyList();
326                                                 }
327                                         }
328
329                                 } else {
330                                         throw new UnsupportedOperationException("");
331                                 }
332                         }
333                 }
334
335         }
336         
337         if(child instanceof StandardProceduralChildVariable) {
338                 
339                 Variable conn = child.getPossibleProperty(graph, cp);
340             FixedConnection fc = (FixedConnection)conn.getValue(graph);
341             Set<VariableConnectionPointDescriptor> result = new THashSet<VariableConnectionPointDescriptor>(1+fc.cps.size());
342             result.add(new ComponentConnectionDescriptor(child, cp));// (graph, STR, curConfiguration, "/" + c.name + "#" + conn.getName(graph)));
343             for(Pair<String,Resource> cpzz : fc.cps) {
344                 if(cpzz.first == null) {
345                         throw new DatabaseException("Lifted connection was not resolved.");
346                 }
347                 result.add(new PairConnectionDescriptor(curConfiguration, cpzz));
348             }
349             return result;
350             
351         } else {
352
353             Resource res = cp;
354             Resource represents = child.getRepresents(graph);
355
356                 if(isStructural) {
357
358                         Collection<Resource> conns = graph.getObjects(represents, res);
359                         HashSet<VariableConnectionPointDescriptor> result = new HashSet<VariableConnectionPointDescriptor>();
360                         for(Resource c : conns) {
361                                 List<Resource> rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, c), TransientCacheAsyncListener.<List<Resource>>instance()); 
362                                 result.addAll(graph.syncRequest(ConnectionVariables.forStructural(graph, curConfiguration, rs)));
363                         }
364                         return result;
365                         
366                 } else {
367
368                 Resource connection = graph.getPossibleObject(represents, res);
369                 if(connection != null) {
370                         List<Resource> rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, connection), TransientCacheAsyncListener.<List<Resource>>instance());
371                         return graph.syncRequest(ConnectionVariables.forConfiguration(graph, curConfiguration, rs));
372                 }
373                 else {
374                         Collection<Resource> conns = graph.getObjects(represents, res);
375                         HashSet<VariableConnectionPointDescriptor> result = new HashSet<VariableConnectionPointDescriptor>();
376                         for(Resource c : conns) {
377                                 List<Resource> rs = graph.syncRequest(new ConnectionComponentsWithAncestor(graph, c), TransientCacheAsyncListener.<List<Resource>>instance()); 
378                                 result.addAll(graph.syncRequest(ConnectionVariables.forConfiguration(graph, curConfiguration, rs)));
379                         }
380                         return result;
381                 }
382                         
383                 }
384                 
385         }
386         
387     }
388     
389     public static void reportDescriptor(ReadGraph graph, VariableConnectionPointDescriptor d) throws DatabaseException {
390
391         if(d instanceof ActualConnectionDescriptor) {
392                 ActualConnectionDescriptor d2 = (ActualConnectionDescriptor)d; 
393                 
394                 System.err.println("--ActualConnectionPointDescriptor2");
395                 System.err.println("---root: " + d2.root.getURI(graph));
396                 System.err.println("---component: " + graph.getPossibleURI(d2.component));
397                 System.err.println("---type: " + graph.getPossibleURI(d2.componentType));
398                 System.err.println("---cp: " + graph.getPossibleURI(d2.cp));
399                 System.err.println("---var: " + d2.getVariable(graph).getURI(graph));
400         }
401         
402     }
403     
404     public static class ConnectionVariables extends BinaryRead<Variable, List<Resource>, Collection<VariableConnectionPointDescriptor>> {
405
406         private ConnectionVariables(Variable parameter1, List<Resource> parameter2) {
407                         super(parameter1, parameter2);
408                 }
409
410                 public static ConnectionVariables forConfiguration(ReadGraph graph, Variable configuration, List<Resource> rs) throws DatabaseException {
411                         return new ConnectionVariables(parent(graph, configuration, rs.get(0)), rs);
412                 }
413
414                 public static ConnectionVariables forStructural(ReadGraph graph, Variable configuration, List<Resource> rs) throws DatabaseException {
415                         return new ConnectionVariables(configuration, rs);
416                 }
417
418                 /**
419                  * Finds the parent variable of <code>configuration</code> that
420                  * represents <code>ancestor</code>.
421                  * 
422                  * @param graph
423                  * @param configuration
424                  * @param ancestor
425                  * @return
426                  * @throws DatabaseException if no parent was found that represents ancestor
427                  */
428                 private static Variable parent(ReadGraph graph, Variable configuration, Resource ancestor) throws DatabaseException {
429                 Variable v = configuration;
430                 Resource represents = v.getRepresents(graph);
431                 while(!represents.equals(ancestor)) {
432                         v = v.getParent(graph);
433                         if (v == null) {
434                                         throw new DatabaseException(
435                                                         "parent representing ancestor not found for variable, configuration="
436                                                                         + safeURI(graph, configuration)
437                                                                         + ", ancestor="
438                                                                         + NameUtils.getURIOrSafeNameInternal(graph, ancestor));
439                         }
440                         represents = v.getRepresents(graph);
441                 }
442                 return v;
443                 }
444
445                 @Override
446                 public Collection<VariableConnectionPointDescriptor> perform(ReadGraph graph) throws DatabaseException {
447                         if(parameter == null) return Collections.emptyList();
448                 Layer0 L0 = Layer0.getInstance(graph);
449                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
450             ArrayList<VariableConnectionPointDescriptor> result = null;
451             for(int i=1;i<parameter2.size();i++) {
452                 Resource connRes = parameter2.get(i);
453                 for(Statement stm : graph.getStatements(connRes, STR.Connects)) {
454                         Resource component = stm.getObject();
455                         Resource connectionPoint = graph.getInverse(stm.getPredicate());
456                         if(result == null) result = new ArrayList<VariableConnectionPointDescriptor>();
457                         String componentName = graph.getRelatedValue(component, L0.HasName, Bindings.STRING);
458                         Variable possibleChild = parameter.getPossibleChild(graph, componentName);
459                         if(possibleChild != null) {
460                             Resource type = possibleChild.getPossibleType(graph, STR.Component);
461                             if(type != null) {
462                             result.add(new ActualConnectionDescriptor(parameter, component, possibleChild.getType(graph), connectionPoint));
463                             } else {
464                             throw new DatabaseException("Child does not have a structural type: " + possibleChild.getURI(graph));
465                             }
466                         } else {
467                             Resource type = graph.getPossibleType(component, STR.Component);
468                             if(type != null) {
469                             result.add(new ActualConnectionDescriptor(parameter, component, type, connectionPoint));
470                             } else {
471                             throw new DatabaseException("Child with name " + componentName + " does not have a structural type: " + parameter.getURI(graph));
472                             }
473                         }
474                 }
475             }
476             if(result == null) return Collections.emptyList();
477             return result;
478                 }
479         
480     }
481     
482     static class IsLeafType extends ResourceRead<Boolean> {
483
484                 protected IsLeafType(Resource type) {
485                         super(type);
486                 }
487
488                 @Override
489                 public Boolean perform(ReadGraph graph) throws DatabaseException {
490                         
491                         StructuralComponentClass clazz = StructuralComponentClass.get(graph, resource);
492                         return StructuralComponentClass.PRIMITIVE.equals(clazz);
493
494                 }
495         
496     }
497     
498     static class ChildMapOfVariable extends VariableRead<Map<Resource,Variable>> {
499
500                 public ChildMapOfVariable(Variable variable) {
501                         super(variable);
502                 }
503
504                 @Override
505                 public Map<Resource, Variable> perform(ReadGraph graph) throws DatabaseException {
506                         HashMap<Resource,Variable> result = new HashMap<Resource,Variable>();
507                         for(Variable child : variable.getChildren(graph)) {
508                                 Resource represents = child.getPossibleRepresents(graph);
509                                 if(represents != null) result.put(represents, child);
510                         }
511                         return result;
512                 }
513         
514     }
515
516     /**
517      * Given a root composite, related variable and some other component inside the composite,
518      * finds the related variable for that component.
519      */
520     public static Variable browse(ReadGraph graph, Resource root, Variable rootContext, Resource target) throws DatabaseException {
521         if(target.equals(root))
522             return rootContext;
523         else {
524             Layer0 L0 = Layer0.getInstance(graph);
525             String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING);
526             Resource parent = graph.getPossibleObject(target, L0.PartOf);
527             if(name == null || parent == null)
528                 return null;
529             Variable parentVariable = browse(graph, root, rootContext, parent);
530             if(parentVariable == null)
531                 return null;
532             return parentVariable.getPossibleChild(graph, name);
533         }
534     }
535
536     /**
537      * Finds a variable whose location related to sourceContext is the same as 
538      * between target and source. In other words, the method solves {@code targetContext}
539      * in the following equations:
540      * <pre>
541      *     URI(source)        = resourceURIBase + sourceSuffix
542      *     URI(sourceContext) = variableURIBase + sourceSuffix
543      *     URI(target)        = resourceURIBase + targetSuffix
544      *     URI(targetContext) = variableURIBase + targetSuffix
545      * </pre>
546      */
547     public static Variable browseSibling(ReadGraph graph, Resource source, Variable sourceContext, Resource target) throws DatabaseException {
548         Layer0 L0 = Layer0.getInstance(graph);
549         THashMap<Resource, Variable> sourceMap = new THashMap<Resource, Variable>();
550         while(source != null && sourceContext != null) {
551             sourceMap.put(source, sourceContext);
552             source = graph.getPossibleObject(source, L0.PartOf);
553             sourceContext = sourceContext.getParent(graph);
554         }
555         return browseSibling(graph, sourceMap, target);
556     }
557
558     private static Variable browseSibling(ReadGraph graph, THashMap<Resource, Variable> sourceMap, Resource target) throws DatabaseException {
559         Layer0 L0 = Layer0.getInstance(graph);
560         Variable result = sourceMap.get(target);
561         if(result != null)
562             return result;
563         String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING);
564         Resource parent = graph.getPossibleObject(target, L0.PartOf);
565         if(name == null || parent == null)
566             return null;
567         Variable parentVariable = browseSibling(graph, sourceMap, parent);
568         if(parentVariable == null)
569             return null;
570         return parentVariable.getPossibleChild(graph, name);
571     }
572
573     /**
574      * Returns the composite where the connection given as a parameter resides.
575      */
576     public static Resource getCompositeOfConnection(ReadGraph graph, Resource connection) throws DatabaseException {
577         Layer0 L0 = Layer0.getInstance(graph);
578         StructuralResource2 STR = StructuralResource2.getInstance(graph);
579         // First from connected components
580         for(Resource component : graph.getObjects(connection, STR.Connects))
581             for(Resource composite : graph.getObjects(component, L0.PartOf))
582                 return composite;
583         // It could be that the connection is only supported by joins (input flag -> output flag) - use diagram info TODO!!
584         Resource connToDiagramConn = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/ConnectionToDiagramConnection");
585         if(connToDiagramConn != null) {
586             Resource diagramConnection = graph.getPossibleObject(connection, connToDiagramConn);
587             if(diagramConnection != null) {
588                 Resource diagram = graph.getPossibleObject(diagramConnection, L0.PartOf);
589                 if(diagram != null) {
590                     Resource diagramToComposite = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/DiagramToComposite");
591                     if(diagramToComposite != null) {
592                         return graph.getPossibleObject(diagram, diagramToComposite);
593                     }
594                 }
595             }
596         }
597         return null;
598     }
599     
600         static class Flatten extends BinaryRead<Variable,Resource,Collection<VariableConnectionPointDescriptor>> {
601
602                 public Flatten(Variable parameter1,
603                                 Resource parameter2) {
604                         super(parameter1, parameter2);
605                 }
606
607                 @Override
608                 public Collection<VariableConnectionPointDescriptor> perform(ReadGraph graph)
609                                 throws DatabaseException {
610                         return doFlatten(graph, parameter, parameter2, null);
611                 }
612                 
613         }
614     
615     public static Collection<VariableConnectionPointDescriptor> flatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException {
616
617         if(relationType == null) return graph.syncRequest(new Flatten(child, cp));
618
619         return doFlatten(graph, child, cp, relationType);
620
621     }
622
623         public static Collection<VariableConnectionPointDescriptor> doFlatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException {
624         
625         Set<VariableConnectionPointDescriptor> result = null;
626         Set<VariableConnectionPointDescriptor> needDrill = null;
627         
628                 Collection<VariableConnectionPointDescriptor> climbed = climb(graph, child, cp, null);
629         for(VariableConnectionPointDescriptor desc : climbed) {
630                 if(!desc.isLeaf(graph)) {
631                         if(needDrill == null)
632                                 needDrill = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
633                         needDrill.add(desc);
634                 } else {
635                         if(result == null)
636                                 result = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
637                         result.add(desc);
638                 }
639         }
640         
641         if(needDrill == null) {
642                 /*
643                  * All descriptors were already flat - just take case of filtering
644                  */
645             if(relationType != null) {
646                 ArrayList<VariableConnectionPointDescriptor> filtered = new ArrayList<VariableConnectionPointDescriptor>(climbed.size());
647                 for(VariableConnectionPointDescriptor desc : climbed)
648                     if(filterByRelationType(graph, desc, relationType))
649                         filtered.add(desc);
650                 return filtered;
651             } else {
652                 return climbed;
653             }
654         }
655         
656
657         /*
658          * There were some descriptors that require drill
659          */
660         for(VariableConnectionPointDescriptor top : needDrill) {
661                 Collection<VariableConnectionPointDescriptor> drilled = drill(graph, top);
662             if(drilled != null) {
663                 for(VariableConnectionPointDescriptor drill : drilled) {
664                         if(relationType != null) {
665                                 if(!filterByRelationType(graph, drill, relationType))
666                                         continue;
667                         }
668                         if(result == null)
669                                 result = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
670                         result.add(drill);
671                 }
672             }
673         }
674         return result;
675         
676     }
677     
678     private static boolean filterByRelationType(ReadGraph graph, VariableConnectionPointDescriptor desc, Resource relationType) throws DatabaseException {
679         Resource predicateResource = desc.getConnectionPointResource(graph);
680         return predicateResource != null && graph.isInstanceOf(predicateResource, relationType);
681     }
682
683     private static String safeURI(ReadGraph graph, Variable v) {
684         if (v == null)
685             return "null variable";
686         try {
687             return v.getURI(graph);
688         } catch (DatabaseException e) {
689             return v.toString();
690         }
691     }
692 }