]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural2/src/org/simantics/structural2/Functions.java
Multiple readers and variable optimization
[simantics/platform.git] / bundles / org.simantics.structural2 / src / org / simantics / structural2 / Functions.java
1 package org.simantics.structural2;
2
3
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
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.databoard.Datatypes;
14 import org.simantics.databoard.adapter.AdaptException;
15 import org.simantics.databoard.binding.Binding;
16 import org.simantics.databoard.binding.error.BindingException;
17 import org.simantics.databoard.binding.error.DatatypeConstructionException;
18 import org.simantics.databoard.type.Datatype;
19 import org.simantics.databoard.util.URIStringUtils;
20 import org.simantics.db.Issue;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.Resource;
23 import org.simantics.db.Statement;
24 import org.simantics.db.WriteGraph;
25 import org.simantics.db.common.issue.StandardIssue;
26 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
27 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
28 import org.simantics.db.common.request.ObjectsWithType;
29 import org.simantics.db.common.request.PossibleIndexRoot;
30 import org.simantics.db.common.request.ResourceRead;
31 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
32 import org.simantics.db.exception.DatabaseException;
33 import org.simantics.db.layer0.function.All;
34 import org.simantics.db.layer0.function.StandardChildDomainChildren;
35 import org.simantics.db.layer0.request.PropertyInfo;
36 import org.simantics.db.layer0.request.PropertyInfoRequest;
37 import org.simantics.db.layer0.request.VariableRead;
38 import org.simantics.db.layer0.variable.AbstractChildVariable;
39 import org.simantics.db.layer0.variable.AbstractPropertyVariable;
40 import org.simantics.db.layer0.variable.LazyPropertyVariable;
41 import org.simantics.db.layer0.variable.NodeSupport;
42 import org.simantics.db.layer0.variable.StandardGraphChildVariable;
43 import org.simantics.db.layer0.variable.StandardGraphPropertyVariable;
44 import org.simantics.db.layer0.variable.ValueAccessor;
45 import org.simantics.db.layer0.variable.ValueAccessorWithBinding;
46 import org.simantics.db.layer0.variable.Variable;
47 import org.simantics.db.layer0.variable.VariableMap;
48 import org.simantics.db.layer0.variable.VariableMapImpl;
49 import org.simantics.db.layer0.variable.VariableNode;
50 import org.simantics.issues.common.IssueUtils;
51 import org.simantics.layer0.Layer0;
52 import org.simantics.scl.reflection.annotations.SCLValue;
53 import org.simantics.scl.runtime.SCLContext;
54 import org.simantics.scl.runtime.function.Function1;
55 import org.simantics.simulation.ontology.SimulationResource;
56 import org.simantics.simulator.variable.NodeManager;
57 import org.simantics.structural.stubs.StructuralResource2;
58 import org.simantics.structural2.procedural.Component;
59 import org.simantics.structural2.procedural.ConnectionPoint;
60 import org.simantics.structural2.procedural.Interface;
61 import org.simantics.structural2.procedural.SubstructureElement;
62 import org.simantics.structural2.procedural.Terminal;
63 import org.simantics.structural2.queries.ConnectionComponents;
64 import org.simantics.structural2.queries.ConnectionJoinComponents;
65 import org.simantics.structural2.queries.ConnectionPointMapOfResource;
66 import org.simantics.structural2.queries.PossibleConnectionPointInfo;
67 import org.simantics.structural2.scl.CompileStructuralValueRequest;
68 import org.simantics.structural2.scl.procedural.CompileProceduralComponentTypeRequest;
69 import org.simantics.structural2.variables.Connection;
70 import org.simantics.structural2.variables.StandardProceduralChildVariable;
71 import org.simantics.utils.datastructures.MapList;
72
73 import gnu.trove.map.hash.THashMap;
74
75 public class Functions {
76         
77         @SCLValue(type="ValueAccessor")
78         public static final ValueAccessor expressionValueAccessor = new ValueAccessorWithBinding() {
79
80                 public Binding getBinding() {
81                         return Bindings.STRING;
82                 }
83                 
84                 @Override
85                 public void setValue(WriteGraph graph, Variable context, Object value)
86                                 throws DatabaseException {
87                         if(value == null) {
88                                 if(getValue(graph, context) != null)
89                                         clearExpression(graph, context);
90                                 return;
91                         }
92                         
93                         // Get all necessary data
94                         String expression = (String)value;
95                         Variable parent = context.getParent(graph);
96                         if(!(parent instanceof AbstractPropertyVariable)) return;
97                         AbstractPropertyVariable property = (AbstractPropertyVariable)parent;                   
98                         Resource propertyResource = property.getRepresents(graph);
99                         if(propertyResource == null) return;
100                         Resource container = property.getContainerResource(graph);
101                         if(container == null) return;
102                         Resource predicate = property.getPossiblePredicateResource(graph);
103                         if(predicate == null) return;                   
104                         Statement stat = graph.getPossibleStatement(container, predicate);                      
105                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
106                         
107                         // Write                        
108                         boolean createNew = false;
109                         if(stat.isAsserted(container))
110                                 createNew = true;
111                         else if(!graph.isInstanceOf(propertyResource, STR.SCLValue)) {
112                                 graph.deny(propertyResource);
113                                 createNew = true;
114                         }
115             Layer0 L0 = Layer0.getInstance(graph);
116                         if(createNew) {
117                                 propertyResource = graph.newResource();
118                                 graph.claim(container, predicate, propertyResource);
119                                 graph.claim(propertyResource, L0.InstanceOf, STR.SCLValue);                             
120                         }
121                         graph.claimLiteral(propertyResource, L0.SCLValue_expression, expression, Bindings.STRING);
122                 }
123
124                 private void clearExpression(WriteGraph graph, Variable context) throws DatabaseException {
125                         Variable parent = context.getParent(graph);
126                         if(!(parent instanceof AbstractPropertyVariable)) return;
127                         AbstractPropertyVariable property = (AbstractPropertyVariable)parent;
128                         Resource container = property.getContainerResource(graph);
129                         if(container == null) return;
130                         Resource predicate = property.getPossiblePredicateResource(graph);
131                         if(predicate == null) return;                   
132                         graph.deny(container, predicate);               
133                 }
134
135                 @Override
136                 public Object getValue(ReadGraph graph, Variable context)
137                                 throws DatabaseException {
138                         Variable parent = context.getParent(graph);
139                         if(!(parent instanceof AbstractPropertyVariable))
140                                 return null;
141                         AbstractPropertyVariable property = (AbstractPropertyVariable)parent;
142                         Resource propertyResource = property.getPossibleRepresents(graph);
143                         if(propertyResource == null) return null;
144                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
145                         if(!graph.isInstanceOf(propertyResource, STR.SCLValue))
146                                 return null;
147             Layer0 L0 = Layer0.getInstance(graph);
148                         return graph.getPossibleRelatedValue(propertyResource, L0.SCLValue_expression);
149                 }
150                 
151         };
152         
153         @SCLValue(type="ValueAccessor")
154         public static final ValueAccessor connectionValueAccessor = new ValueAccessor() {
155
156                 @Override
157                 public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
158                         throw new UnsupportedOperationException();
159                 }
160                 
161                 public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException {
162                         throw new UnsupportedOperationException();
163                 }
164
165                 public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
166             try {
167                 Object value = getValue(graph, context);
168                 Binding srcBinding = Bindings.OBJECT.getContentBinding(value);
169                                 return Bindings.adapt(value, srcBinding, binding);
170                         } catch (AdaptException e) {
171                                 throw new DatabaseException(e);
172                         } catch (BindingException e) {
173                                 throw new DatabaseException(e);
174                         }
175                 }
176
177                 @Override
178                 public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
179                         StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; 
180                         return new ConnectionImpl(variable.property.predicate);
181                 }
182
183                 @Override
184                 public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
185                         try {
186                                 return Datatypes.getDatatype(Connection.class);
187                         } catch (DatatypeConstructionException e) {
188                                 throw new DatabaseException(e);
189                         }
190                 }
191                 
192         };      
193
194
195         @SCLValue(type = "VariableMap")
196         public static VariableMap structuralChildDomainProperties = new VariableMapImpl() {
197         
198                 public Variable getPossibleConnectionPointFromContext(ReadGraph graph, Variable variable, Resource context, String name) throws DatabaseException {
199
200                         Map<String, PropertyInfo> connectionPoints = graph.syncRequest(new ConnectionPointMapOfResource(graph, context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance());
201                         PropertyInfo cp = connectionPoints.get(name);
202                         if(cp == null) return null;
203                         else return new StandardGraphPropertyVariable(graph, variable, cp.predicate);
204                         
205                 }
206                 
207                 public Map<String, Variable> collectConnectionPointsFromContext(ReadGraph graph, StructuralResource2 STR, Variable variable, Resource context, Map<String, Variable> map, boolean needSynchronized) throws DatabaseException {
208                         
209                         if(graph.isImmutable(context)) {
210
211                                 Map<String, PropertyInfo> cps = graph.syncRequest(new ConnectionPointMapOfResource(graph, context), TransientCacheAsyncListener.<Map<String,PropertyInfo>>instance());
212                                 if(cps.size() == 0) return map;
213                                 
214                                 if(map == null) map = new THashMap<String,Variable>(cps.size());
215                                 
216                                 for(Map.Entry<String, PropertyInfo> entry : cps.entrySet()) {
217                                         String name = entry.getKey();
218                                         PropertyInfo cp = entry.getValue();
219                                         if(needSynchronized && !graph.isInstanceOf(cp.predicate, STR.SynchronizedConnectionRelation)) continue;
220                                         map.put(name, new StandardGraphPropertyVariable(graph, variable, cp.predicate));
221                                 }
222                                 
223                                 return map;
224
225                         } else {
226                                 
227                                 Collection<Resource> predicates = graph.getPredicates(context);
228                                 
229                                 for(Resource predicate : predicates) {
230                                         
231                                         PropertyInfo info = graph.isImmutable(predicate) ?
232                                                         graph.syncRequest(new PossibleConnectionPointInfo(predicate), TransientCacheAsyncListener.<PropertyInfo>instance()) :
233                                                                 graph.syncRequest(new PossibleConnectionPointInfo(predicate));
234                                                         
235                                         if(info != null) {
236                                                 if(map == null) map = new THashMap<String,Variable>(4);
237                                                 if(needSynchronized && !graph.isInstanceOf(predicate, STR.SynchronizedConnectionRelation)) continue;
238                                                 map.put(info.name, new StandardGraphPropertyVariable(graph, variable, predicate));
239                                         }
240                                         
241                                 }
242
243                                 return map;
244
245                         }
246                         
247                 }
248
249                 @Override
250                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
251                 final StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
252                 Variable cp = getPossibleConnectionPointFromContext(graph, variable, variable.resource, name);
253                 if(cp != null) return cp;
254             return All.getStandardChildDomainPropertyVariable(graph, context, name);
255                 }
256
257                 @Override
258                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
259                 StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
260                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
261                 map = collectConnectionPointsFromContext(graph, STR, variable, variable.resource, map, false);
262             return All.getStandardChildDomainPropertyVariables(graph, context, map);
263                 }
264                 
265                 public Map<String,Variable> getVariables(ReadGraph graph, Variable context, String classification, Map<String,Variable> map) throws DatabaseException {
266                         if (StructuralResource2.URIs.SynchronizedRelation.equals(classification)) {
267                                 return All.getStandardChildDomainPropertyVariables(graph, context, classification, map);
268                         } else if (StructuralResource2.URIs.SynchronizedConnectionRelation.equals(classification)) {
269                         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
270                         return collectConnectionPointsFromContext(graph, StructuralResource2.getInstance(graph), variable, variable.resource, map, true);
271                         } else if(StructuralResource2.URIs.ConnectionRelation.equals(classification)) {
272                         StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
273                         return collectConnectionPointsFromContext(graph, StructuralResource2.getInstance(graph), variable, variable.resource, map, false);
274                         }
275                         return super.getVariables(graph, context, classification, map);
276                 }
277                 
278         };
279
280         public static class StructuralChildMapOfResource extends ResourceRead<Map<String, Resource>> {
281
282                 public StructuralChildMapOfResource(Resource resource) {
283                         super(resource);
284                 }
285
286                 @Override
287                 public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
288                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
289                         Resource type = graph.getPossibleType(resource, STR.Component);
290                         if(type != null) {
291                                 Resource definition = graph.getPossibleObject(type, STR.IsDefinedBy);
292                                 if(definition != null) {
293                                         Map<String, Resource> map = graph.syncRequest(new UnescapedChildMapOfResource(definition));
294                                         if (!map.isEmpty())
295                                                 return map;
296                                 } 
297                         }
298                         Map<String, Resource> directChildren = graph.syncRequest(new UnescapedChildMapOfResource(resource));
299                         return directChildren;
300                 }
301
302         }
303
304     public static class StructuralChildMapOfResourceT extends ResourceRead<Map<String, Resource>> {
305
306         public StructuralChildMapOfResourceT(Resource resource) {
307             super(resource);
308         }
309
310         @Override
311         public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
312             StructuralResource2 STR = StructuralResource2.getInstance(graph);
313             Resource definition = graph.getPossibleObject(resource, STR.IsDefinedBy);
314             if(definition != null) {
315                 Map<String, Resource> map = graph.syncRequest(new UnescapedChildMapOfResource(definition));
316                 if (!map.isEmpty())
317                     return map;
318             } 
319             return Collections.emptyMap();
320         }
321
322     }
323
324         static class StructuralRunChildMapOfResource extends ResourceRead<Map<String, Resource>> {
325
326                 public StructuralRunChildMapOfResource(Resource resource) {
327                         super(resource);
328                 }
329
330                 public Map<String, Resource> fromContext(ReadGraph graph, Resource context) throws DatabaseException {
331                         return graph.sync(new StructuralChildMapOfResource(context));
332                 }
333                 
334                 @Override
335                 public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
336
337                         Layer0 L0 = Layer0.getInstance(graph);
338                         SimulationResource SIMU = SimulationResource.getInstance(graph);
339                         Resource model = graph.sync(new PossibleIndexRoot(resource));
340                         if(graph.isInstanceOf(model, L0.RVIContext)) {
341                                 return fromContext(graph, model);
342                         }
343                         Resource configuration = graph.getPossibleObject(model, SIMU.HasConfiguration);
344                         if(configuration != null) {
345                                 if(graph.isInstanceOf(configuration, L0.RVIContext)) {
346                                         return fromContext(graph, configuration);
347                                 }
348                         }
349                         
350                         return Collections.emptyMap();
351                         
352                 }
353                 
354         }
355         
356         private static class SubstructureRequest extends VariableRead<List<SubstructureElement>> {
357             public SubstructureRequest(Variable context) {
358             super(context);
359         }
360
361         @Override
362         public List<SubstructureElement> perform(ReadGraph graph) {
363             try {
364                 Resource type = variable.getPossibleType(graph);
365                 if(type == null)
366                     return null;
367                 return CompileProceduralComponentTypeRequest.compileAndEvaluate(graph, type, variable);
368             } catch (Throwable t) {
369                 t.printStackTrace();
370                 return null;
371             }
372         }
373         }
374
375     public static List<SubstructureElement> getProceduralDesc(ReadGraph graph, final Variable context) throws DatabaseException {
376         StructuralResource2 STR = StructuralResource2.getInstance(graph);
377         final Resource type = context.getPossibleType(graph);
378         if(type != null) {
379             if(graph.isInstanceOf(type, STR.ProceduralComponentType)) {
380                 return graph.syncRequest(new SubstructureRequest(context));
381             }
382         }
383         return null;
384     }  
385         
386          public static Map<String,Variable> getProcedural(ReadGraph graph, Variable context, List<SubstructureElement> elements, Map<String,Variable> map) throws DatabaseException {
387              
388          if(map == null) map = new THashMap<String,Variable>();
389          
390          MapList<String,org.simantics.structural2.procedural.Connection> conns = new MapList<String,org.simantics.structural2.procedural.Connection>();
391          for(SubstructureElement sub : elements) {
392              if(sub instanceof org.simantics.structural2.procedural.Connection) {
393                  org.simantics.structural2.procedural.Connection conn = (org.simantics.structural2.procedural.Connection)sub;
394                  for(ConnectionPoint cp : conn.connectionPoints) {
395                      if(cp instanceof Terminal) {
396                          Terminal t = (Terminal)cp;
397                          conns.add(t.component, conn);
398                      }
399                  }
400              }
401          }
402          
403          Map<String,Component> proceduralChildren = new THashMap<String, Component>();
404          for(SubstructureElement sub : elements) {
405              if(sub instanceof Component) {
406                  Component comp = (Component)sub;
407                  proceduralChildren.put(comp.name, comp);
408              }
409          }
410          
411          Collection<Object> nodeChildren = All.getPossibleNodeChildren(graph, (AbstractChildVariable)context);
412          Set<String> used = new HashSet<String>(nodeChildren.size());
413          for(Object nodeChild : nodeChildren) {
414              @SuppressWarnings("rawtypes")
415              NodeSupport support = ((AbstractChildVariable)context).node.support;
416              @SuppressWarnings("rawtypes")
417              NodeManager manager = support.manager;
418              @SuppressWarnings("unchecked")
419              String name = manager.getName(nodeChild);
420              used.add(name);
421              Component proceduralChild = proceduralChildren.get(name); 
422              if(proceduralChild != null) {
423                  map.put(proceduralChild.name, new StandardProceduralChildVariable(graph, context, new VariableNode(support, nodeChild), proceduralChild.name, proceduralChild.type, proceduralChild.properties, conns.getValues(proceduralChild.name)));
424              }
425          }
426          
427          for(Map.Entry<String, Component> entry : proceduralChildren.entrySet()) {
428              String name = entry.getKey();
429              if(used.contains(name)) continue;
430              Component proceduralChild = entry.getValue();
431              map.put(proceduralChild.name, new StandardProceduralChildVariable(graph, context, null, proceduralChild.name, proceduralChild.type, proceduralChild.properties, conns.getValues(proceduralChild.name)));
432          }
433          
434          return map;
435              
436      }  
437          
438          private static class ProceduralSubstructureRequest extends VariableRead<Map<String,Variable>> {
439
440              public ProceduralSubstructureRequest(Variable variable) {
441                  super(variable);
442              }
443
444              @Override
445              public Map<String, Variable> perform(ReadGraph graph)
446                      throws DatabaseException {
447                  List<SubstructureElement> elements = getProceduralDesc(graph, variable);
448                  if(elements != null)
449                      return getProcedural(graph, variable, elements, null);
450                  else
451                      return null;
452              }
453          }
454          
455         
456         @SCLValue(type = "VariableMap")
457         public static VariableMap structuralChildDomainChildren = new VariableMapImpl() {
458         
459                 @Override
460                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
461                         
462                 final Resource type = context.getPossibleType(graph);
463                 if(type != null) {
464                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
465                     if(graph.isInstanceOf(type, STR.ProceduralComponentType)) {
466                             Map<String,Variable> map = graph.syncRequest(new ProceduralSubstructureRequest(context),
467                                 TransientCacheListener.<Map<String,Variable>>instance());
468                             if(map != null) return map.get(name);
469                     }
470                 }
471
472                     Resource represents = context.getPossibleRepresents(graph);
473             if(represents == null) {
474                 Map<String, Resource> children = graph.syncRequest(new StructuralChildMapOfResourceT(type));
475                 Resource child = children.get(name);
476                 return All.getStandardChildDomainChildVariable(graph, context, child, name);
477             }
478             Map<String, Resource> children = graph.syncRequest(new StructuralChildMapOfResource(represents));
479             Resource child = children.get(name);
480             return All.getStandardChildDomainChildVariable(graph, context, child, name);
481                 }
482
483                 @Override
484                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
485                         
486                 final Resource type = context.getPossibleType(graph);
487                 if(type != null) {
488                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
489                     if(graph.isInstanceOf(type, STR.ProceduralComponentType)) {
490                         Map<String,Variable> mapPrime = graph.syncRequest(new ProceduralSubstructureRequest(context),
491                                         TransientCacheListener.<Map<String,Variable>>instance());
492                         if(mapPrime != null) {
493                                 if(map != null) {
494                                         map.putAll(mapPrime);
495                                         return map;
496                                 }
497                                 else
498                                         return mapPrime;
499                         }
500                     }
501                 }
502             Resource represents = context.getPossibleRepresents(graph);
503             if(represents == null) {
504                 Map<String, Resource> children = graph.syncRequest(new StructuralChildMapOfResourceT(type));
505                 return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
506             }
507             Map<String, Resource> children = graph.syncRequest(new StructuralChildMapOfResource(represents));
508             return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
509                 }
510                 
511         };
512         
513         @SCLValue(type = "VariableMap")
514         public static VariableMap structuralRunDomainChildren = new VariableMapImpl() {
515         
516                 @Override
517                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
518                     Map<String, Resource> children = graph.syncRequest(new StructuralRunChildMapOfResource(context.getRepresents(graph)));
519                         Resource child = children.get(name);
520             return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, child, name);
521                 }
522
523                 @Override
524                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
525             StandardGraphChildVariable variable = (StandardGraphChildVariable)context;
526             Map<String,Resource> children = graph.syncRequest(new StructuralRunChildMapOfResource(variable.resource));
527                     return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
528                 }
529                 
530         };
531
532         @SCLValue(type = "ReadGraph -> [Resource] -> [Resource]")
533     public static List<Resource> connectionExtension(ReadGraph graph, List<Resource> rs) throws DatabaseException {
534
535         StructuralResource2 STR = StructuralResource2.getInstance(graph);
536         HashSet<Resource> extension = new HashSet<Resource>(8);
537         for(Resource r : rs) {
538                 if(graph.isInstanceOf(r, STR.Connection)) {
539                         extension.addAll(graph.syncRequest(new ConnectionComponents(r), TransientCacheListener.<Collection<Resource>>instance()));
540                 }
541                 if(graph.isInstanceOf(r, STR.ConnectionJoin)) {
542                         extension.addAll(graph.syncRequest(new ConnectionJoinComponents(r), TransientCacheListener.<Collection<Resource>>instance()));
543                 }
544         }
545
546         HashSet<Resource> components = new HashSet<Resource>(8);
547         for(Resource r : extension) {
548                 components.addAll(graph.sync(new ObjectsWithType(r, STR.Connects, STR.Component)));
549         }
550         
551         if(!extension.isEmpty()) {
552                 ArrayList<Resource> result = new ArrayList<Resource>(rs.size() + extension.size());
553                 result.addAll(rs);
554                 result.addAll(extension);
555                 result.addAll(components);
556                 rs = result;
557         }
558         
559         return rs;
560         
561     }
562
563         @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
564     public static List<Issue> connectionValidator(ReadGraph graph, Resource component) throws DatabaseException {
565         
566                 if(!graph.hasStatement(component)) return Collections.emptyList();
567
568                 ArrayList<Issue> result = new ArrayList<Issue>();
569                 
570                 Layer0 L0 = Layer0.getInstance(graph);
571                 StructuralResource2 sr = StructuralResource2.getInstance(graph);
572
573                 Resource type = graph.getSingleType(component, sr.Component);
574                 
575                 Set<Resource> requiredConnections = new HashSet<Resource>();
576                 for(Resource connectionRelation : graph.sync(new ObjectsWithType(type, L0.ConsistsOf, sr.ConnectionRelation))) {
577                         Boolean required = graph.getPossibleRelatedValue(connectionRelation, sr.ConnectionRelation_connectionRequired, Bindings.BOOLEAN);
578                         if(required != null && required)
579                                 requiredConnections.add(connectionRelation);
580                 }
581                 
582                 Set<Resource> connections = new HashSet<Resource>();
583
584                 for(Statement stm : graph.getStatements(component, sr.IsConnectedTo)) {
585                         connections.add(stm.getPredicate());
586                         connections.addAll(graph.getSuperrelations(stm.getPredicate()));
587                 }
588
589                 for(Resource req : requiredConnections) {
590                         if(!connections.contains(req)) {
591                                 result.add(new StandardIssue(sr.ConnectionConstraint_ErrorIssue, component, req));
592                         }
593                 }
594
595                 return result;
596         
597     }
598
599     @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")
600     public static String connectionIssueDescription(ReadGraph graph, Resource converter, Variable property) throws DatabaseException {
601         List<Resource> contexts = IssueUtils.getContextsForProperty(graph, property);
602         String attributeName = graph.getRelatedValue(contexts.get(1), Layer0.getInstance(graph).HasName);
603         return "'" + attributeName + "' should be connected.";
604     }
605
606     public static class InterfacePathMap extends VariableRead<GraphMap<Map<String,InterfaceResolution>>> {
607
608                 public InterfacePathMap(Variable context) {
609                         super(context);
610                 }
611
612                 @Override
613                 public GraphMap<Map<String,InterfaceResolution>> perform(ReadGraph graph) throws DatabaseException {
614
615                         return new GraphMap<Map<String,InterfaceResolution>>() {
616
617                                 @Override
618                                 Map<String, InterfaceResolution> get(ReadGraph graph, String key) throws DatabaseException {
619                                         
620                                         Variable child = variable.getChild(graph, key);
621                                         
622                                         Map<String,InterfaceResolution> childMap = new THashMap<String,InterfaceResolution>();
623                                         Collection<InterfaceResolution> paths = computeInterfacePaths(graph, child);//child.getPossiblePropertyValue(graph, "proceduralConnectionPointPath");
624                                         if(paths != null) {
625                                                 for(InterfaceResolution r : paths) {
626                                                         childMap.put(r.interfaceName, r);
627                                                 }
628                                         }
629                                         return childMap;
630
631                                 }
632                                 
633                         };
634                         
635                 }
636         
637     }
638     
639     public static String resolveInterfacePath(ReadGraph graph, Variable context, String component, Resource relation) throws DatabaseException {
640         
641         GraphMap<Map<String,InterfaceResolution>> map = graph.syncRequest(new InterfacePathMap(context), TransientCacheListener.<GraphMap<Map<String,InterfaceResolution>>>instance());
642         Map<String,InterfaceResolution> childMap = map.get(graph, component);
643         if(childMap == null) return "";
644
645         PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheListener.<PropertyInfo>instance());
646
647         InterfaceResolution match = childMap.get(info.name);
648         if(match != null) {
649                 String comp = URIStringUtils.escape(component);
650                 Variable newContext = context.getChild(graph, component);
651                 return "/" + comp + resolveInterfacePath(graph, newContext, match.componentName, match.connectionPoint);
652         } else {
653                 return "/" + URIStringUtils.escape(component) + "#" + URIStringUtils.escape(info.name); 
654         }
655                 
656     }
657     
658     public static class InterfaceResolution {
659         
660         public Resource interfaceConnectionPoint;
661         public String interfaceName;
662         public String componentName;
663         public Resource connectionPoint;
664         
665         public InterfaceResolution(Resource interfaceConnectionPoint, String interfaceName, String componentName, Resource connectionPoint) {
666                 this.interfaceConnectionPoint = interfaceConnectionPoint;
667                 this.interfaceName = interfaceName;
668                 this.componentName = componentName;
669                 this.connectionPoint = connectionPoint;
670         }
671         
672     }
673     
674     public static Collection<InterfaceResolution> computeInterfacePaths(ReadGraph graph, Variable variable) throws DatabaseException {
675
676                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
677                 Resource type = variable.getPossibleType(graph);
678                 if(type != null) {
679                         if(graph.isInstanceOf(type, STR.ProceduralComponentType)) {
680                                 ArrayList<InterfaceResolution> result = new ArrayList<InterfaceResolution>();
681                                 List<SubstructureElement> elements = getProceduralDesc(graph, variable); 
682                                 if(elements != null) {
683                                         for(SubstructureElement e : elements) {
684                                                 if(e instanceof org.simantics.structural2.procedural.Connection) {
685                                                         org.simantics.structural2.procedural.Connection conn = (org.simantics.structural2.procedural.Connection)e;
686                                                         Interface inf = null;
687                                                         for(ConnectionPoint cp : conn.connectionPoints) {
688                                                                 if(cp instanceof Interface) {
689                                                                         if(inf != null) throw new DatabaseException("Multiple interfaces referenced in procedural connection.");
690                                                                         inf = (Interface)cp;
691                                                                 }
692                                                         }
693                                                         if(inf != null && conn.connectionPoints.size() > 1) {
694                                                                 Layer0 L0 = Layer0.getInstance(graph);
695                                                                 String cpName = URIStringUtils.escape( graph.<String>getRelatedValue(inf.relation, L0.HasName, Bindings.STRING) );
696                                                                 for(ConnectionPoint cp : conn.connectionPoints) {
697                                                                         if(cp == inf) continue;
698                                                                         Terminal t = (Terminal)cp;
699                                                                         result.add(new InterfaceResolution(inf.relation, cpName, t.component, t.relation));
700                                                                 }
701                                                         }
702                                                 }
703                                         }
704                                 }
705
706                                 return result;
707                                 
708                         }
709
710                         final Collection<InterfaceResolution> interfaces = graph.syncRequest(new DefinedUCInterfaceMap(type));
711                         if(interfaces != null) return interfaces;
712
713                 }
714                 
715                 return BUILTIN_STRUCTURAL_CPS;
716         
717     }
718     
719     static class InterfacePathRequest extends VariableRead<Collection<InterfaceResolution>> {
720
721                 public InterfacePathRequest(Variable variable) {
722                         super(variable);
723                 }
724                 
725                 @Override
726                 public Collection<InterfaceResolution> perform(ReadGraph graph) throws DatabaseException {
727                         return computeInterfacePaths(graph, variable);
728                 }
729         
730     }
731     
732     static class DefinedUCInterfaceMap extends ResourceRead<Collection<InterfaceResolution>> {
733
734         public DefinedUCInterfaceMap(Resource resource) {
735                 super(resource);
736         }
737
738         @Override
739         public Collection<InterfaceResolution> perform(ReadGraph graph)
740                         throws DatabaseException {
741
742                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
743                 Resource definition = graph.getPossibleObject(resource, STR.IsDefinedBy);
744                 if(definition != null) {
745                         Collection<InterfaceResolution> result = new ArrayList<InterfaceResolution>();
746                         Layer0 L0 = Layer0.getInstance(graph);
747                         for(Resource cp : graph.syncRequest(new ObjectsWithType(resource, L0.ConsistsOf, STR.ConnectionRelation))) {
748                                         String cpName = graph.getRelatedValue(cp, L0.HasName, Bindings.STRING);
749                                 for(Resource conn : graph.getObjects(cp, STR.IsBoundBy)) {
750                                         Statement stm = graph.getPossibleStatement(conn, STR.Connects);
751                                         if(stm == null) continue;
752                                         Resource component = stm.getObject();
753                                         String componentName = graph.getRelatedValue(component, L0.HasName, Bindings.STRING);
754                                         result.add(new InterfaceResolution(cp, cpName, componentName, graph.getInverse(stm.getPredicate())));
755                                 }
756                         }
757                         return result;
758                 }
759                 return null;
760         }
761
762     }
763     
764     public static final Collection<InterfaceResolution> BUILTIN_STRUCTURAL_CPS = new ArrayList<InterfaceResolution>();
765
766         @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
767     public static Object computeExpression(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
768         return CompileStructuralValueRequest.compileAndEvaluate(graph, context);
769     }
770
771     public static Object computeExpressionInContext(ReadGraph graph, Variable context, final String expression) throws DatabaseException {
772         SCLContext sclContext = SCLContext.getCurrent();
773         Object oldGraph = sclContext.get("graph");
774         try {
775             Function1<Variable,Object> exp = graph.syncRequest(new CompileStructuralValueRequest(graph, context) {
776                 protected String getExpressionText(ReadGraph graph) throws DatabaseException {
777                     return expression;
778                 }
779             },
780             TransientCacheListener.<Function1<Variable,Object>>instance());
781             sclContext.put("graph", graph);
782             return exp.apply(context);
783         } catch (DatabaseException e) {
784             throw (DatabaseException)e;
785         } catch (Throwable t) {
786             throw new DatabaseException(t);
787         } finally {
788             sclContext.put("graph", oldGraph);
789         }
790     }    
791         
792         static abstract class InterfacePathProperty extends LazyPropertyVariable {
793                 
794                 public InterfacePathProperty(Variable parent) {
795                         super(parent, "proceduralConnectionPointPath", Bindings.STRING_ARRAY);
796                 }
797                 
798                 @Override
799                 public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
800                         return getValue(graph);
801                 }
802                 
803         }
804         
805         static abstract class GraphMap<Value> {
806                 
807                 abstract Value get(ReadGraph graph, String key) throws DatabaseException;
808                 
809         }
810     
811 }