]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural2/src/org/simantics/structural2/variables/ConnectionBrowser.java
Replaceable Defined Component Types
[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.getComponentType(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                                 result.add(new ActualConnectionDescriptor(parameter, component, possibleChild.getType(graph), connectionPoint));
460                         } else {
461                                 throw new DatabaseException("No child with name " + componentName + " could be resolved for variable " + parameter.getURI(graph));
462                         }
463                 }
464             }
465             if(result == null) return Collections.emptyList();
466             return result;
467                 }
468         
469     }
470     
471     static class IsLeafType extends ResourceRead<Boolean> {
472
473                 protected IsLeafType(Resource type) {
474                         super(type);
475                 }
476
477                 @Override
478                 public Boolean perform(ReadGraph graph) throws DatabaseException {
479                         
480                         StructuralComponentClass clazz = StructuralComponentClass.get(graph, resource);
481                         return StructuralComponentClass.PRIMITIVE.equals(clazz);
482
483                 }
484         
485     }
486     
487     static class ChildMapOfVariable extends VariableRead<Map<Resource,Variable>> {
488
489                 public ChildMapOfVariable(Variable variable) {
490                         super(variable);
491                 }
492
493                 @Override
494                 public Map<Resource, Variable> perform(ReadGraph graph) throws DatabaseException {
495                         HashMap<Resource,Variable> result = new HashMap<Resource,Variable>();
496                         for(Variable child : variable.getChildren(graph)) {
497                                 Resource represents = child.getPossibleRepresents(graph);
498                                 if(represents != null) result.put(represents, child);
499                         }
500                         return result;
501                 }
502         
503     }
504
505     /**
506      * Given a root composite, related variable and some other component inside the composite,
507      * finds the related variable for that component.
508      */
509     public static Variable browse(ReadGraph graph, Resource root, Variable rootContext, Resource target) throws DatabaseException {
510         if(target.equals(root))
511             return rootContext;
512         else {
513             Layer0 L0 = Layer0.getInstance(graph);
514             String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING);
515             Resource parent = graph.getPossibleObject(target, L0.PartOf);
516             if(name == null || parent == null)
517                 return null;
518             Variable parentVariable = browse(graph, root, rootContext, parent);
519             if(parentVariable == null)
520                 return null;
521             return parentVariable.getPossibleChild(graph, name);
522         }
523     }
524
525     /**
526      * Finds a variable whose location related to sourceContext is the same as 
527      * between target and source. In other words, the method solves {@code targetContext}
528      * in the following equations:
529      * <pre>
530      *     URI(source)        = resourceURIBase + sourceSuffix
531      *     URI(sourceContext) = variableURIBase + sourceSuffix
532      *     URI(target)        = resourceURIBase + targetSuffix
533      *     URI(targetContext) = variableURIBase + targetSuffix
534      * </pre>
535      */
536     public static Variable browseSibling(ReadGraph graph, Resource source, Variable sourceContext, Resource target) throws DatabaseException {
537         Layer0 L0 = Layer0.getInstance(graph);
538         THashMap<Resource, Variable> sourceMap = new THashMap<Resource, Variable>();
539         while(source != null && sourceContext != null) {
540             sourceMap.put(source, sourceContext);
541             source = graph.getPossibleObject(source, L0.PartOf);
542             sourceContext = sourceContext.getParent(graph);
543         }
544         return browseSibling(graph, sourceMap, target);
545     }
546
547     private static Variable browseSibling(ReadGraph graph, THashMap<Resource, Variable> sourceMap, Resource target) throws DatabaseException {
548         Layer0 L0 = Layer0.getInstance(graph);
549         Variable result = sourceMap.get(target);
550         if(result != null)
551             return result;
552         String name = (String)graph.getPossibleRelatedValue(target, L0.HasName, Bindings.STRING);
553         Resource parent = graph.getPossibleObject(target, L0.PartOf);
554         if(name == null || parent == null)
555             return null;
556         Variable parentVariable = browseSibling(graph, sourceMap, parent);
557         if(parentVariable == null)
558             return null;
559         return parentVariable.getPossibleChild(graph, name);
560     }
561
562     /**
563      * Returns the composite where the connection given as a parameter resides.
564      */
565     public static Resource getCompositeOfConnection(ReadGraph graph, Resource connection) throws DatabaseException {
566         Layer0 L0 = Layer0.getInstance(graph);
567         StructuralResource2 STR = StructuralResource2.getInstance(graph);
568         // First from connected components
569         for(Resource component : graph.getObjects(connection, STR.Connects))
570             for(Resource composite : graph.getObjects(component, L0.PartOf))
571                 return composite;
572         // It could be that the connection is only supported by joins (input flag -> output flag) - use diagram info TODO!!
573         Resource connToDiagramConn = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/ConnectionToDiagramConnection");
574         if(connToDiagramConn != null) {
575             Resource diagramConnection = graph.getPossibleObject(connection, connToDiagramConn);
576             if(diagramConnection != null) {
577                 Resource diagram = graph.getPossibleObject(diagramConnection, L0.PartOf);
578                 if(diagram != null) {
579                     Resource diagramToComposite = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/DiagramToComposite");
580                     if(diagramToComposite != null) {
581                         return graph.getPossibleObject(diagram, diagramToComposite);
582                     }
583                 }
584             }
585         }
586         return null;
587     }
588     
589         static class Flatten extends BinaryRead<Variable,Resource,Collection<VariableConnectionPointDescriptor>> {
590
591                 public Flatten(Variable parameter1,
592                                 Resource parameter2) {
593                         super(parameter1, parameter2);
594                 }
595
596                 @Override
597                 public Collection<VariableConnectionPointDescriptor> perform(ReadGraph graph)
598                                 throws DatabaseException {
599                         return doFlatten(graph, parameter, parameter2, null);
600                 }
601                 
602         }
603     
604     public static Collection<VariableConnectionPointDescriptor> flatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException {
605
606         if(relationType == null) return graph.syncRequest(new Flatten(child, cp));
607
608         return doFlatten(graph, child, cp, relationType);
609
610     }
611
612         public static Collection<VariableConnectionPointDescriptor> doFlatten(ReadGraph graph, Variable child, Resource cp, Resource relationType) throws DatabaseException {
613         
614         Set<VariableConnectionPointDescriptor> result = null;
615         Set<VariableConnectionPointDescriptor> needDrill = null;
616         
617                 Collection<VariableConnectionPointDescriptor> climbed = climb(graph, child, cp, null);
618         for(VariableConnectionPointDescriptor desc : climbed) {
619                 if(!desc.isLeaf(graph)) {
620                         if(needDrill == null)
621                                 needDrill = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
622                         needDrill.add(desc);
623                 } else {
624                         if(result == null)
625                                 result = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
626                         result.add(desc);
627                 }
628         }
629         
630         if(needDrill == null) {
631                 /*
632                  * All descriptors were already flat - just take case of filtering
633                  */
634             if(relationType != null) {
635                 ArrayList<VariableConnectionPointDescriptor> filtered = new ArrayList<VariableConnectionPointDescriptor>(climbed.size());
636                 for(VariableConnectionPointDescriptor desc : climbed)
637                     if(filterByRelationType(graph, desc, relationType))
638                         filtered.add(desc);
639                 return filtered;
640             } else {
641                 return climbed;
642             }
643         }
644         
645
646         /*
647          * There were some descriptors that require drill
648          */
649         for(VariableConnectionPointDescriptor top : needDrill) {
650                 Collection<VariableConnectionPointDescriptor> drilled = drill(graph, top);
651             if(drilled != null) {
652                 for(VariableConnectionPointDescriptor drill : drilled) {
653                         if(relationType != null) {
654                                 if(!filterByRelationType(graph, drill, relationType))
655                                         continue;
656                         }
657                         if(result == null)
658                                 result = new THashSet<VariableConnectionPointDescriptor>(climbed.size());
659                         result.add(drill);
660                 }
661             }
662         }
663         return result;
664         
665     }
666     
667     private static boolean filterByRelationType(ReadGraph graph, VariableConnectionPointDescriptor desc, Resource relationType) throws DatabaseException {
668         Resource predicateResource = desc.getConnectionPointResource(graph);
669         return predicateResource != null && graph.isInstanceOf(predicateResource, relationType);
670     }
671
672     private static String safeURI(ReadGraph graph, Variable v) {
673         if (v == null)
674             return "null variable";
675         try {
676             return v.getURI(graph);
677         } catch (DatabaseException e) {
678             return v.toString();
679         }
680     }
681 }