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