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