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