Resolving a variable for name that has no Resource representation fails.
[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                 return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, child, name);
576                         }
577
578                 }
579
580                 @Override
581                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
582                         
583                         Resource type = context.getPossibleType(graph);
584                         if(type == null) return null;
585                         
586                         StructuralComponentClass clazz = StructuralComponentClass.get(graph, type);
587                         if(StructuralComponentClass.PROCEDURAL.equals(clazz)) {
588                 Map<String,Variable> mapPrime = graph.syncRequest(new ProceduralSubstructureRequest(context),
589                                 TransientCacheListener.<Map<String,Variable>>instance());
590                 if(mapPrime != null) {
591                         if(map != null) {
592                                 map.putAll(mapPrime);
593                                 return map;
594                         }
595                         else
596                                 return mapPrime;
597                 }
598                 return map;
599                         } else if (StructuralComponentClass.DEFINED.equals(clazz)) {
600                                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
601                                 Resource def = graph.getSingleObject(type, STR.IsDefinedBy);
602                 Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(def));
603                 return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
604                         } else {
605                                 Resource represents = context.getPossibleRepresents(graph);
606                                 if(represents == null) return null;
607                 Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(represents));
608                 return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
609                         }
610             
611                 }
612                 
613         };
614         
615         @SCLValue(type = "VariableMap")
616         public static VariableMap structuralRunDomainChildren = new VariableMapImpl() {
617         
618                 @Override
619                 public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException {
620                         Resource ctx = graph.syncRequest(new StructuralRunContext(context.getRepresents(graph)));
621                         if(ctx == null) return null;
622                     Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(ctx));
623                         Resource child = children.get(name);
624             return StandardChildDomainChildren.getStandardChildDomainChildVariable(graph, context, child, name);
625                 }
626
627                 @Override
628                 public Map<String, Variable> getVariables(ReadGraph graph, Variable context, Map<String, Variable> map) throws DatabaseException {
629                         Resource ctx = graph.syncRequest(new StructuralRunContext(context.getRepresents(graph)));
630                         if(ctx == null) return map;
631                     Map<String, Resource> children = graph.syncRequest(new UnescapedChildMapOfResource(ctx));
632                     return StandardChildDomainChildren.getStandardChildDomainChildVariables(graph, context, children, map);
633                 }
634                 
635         };
636
637         @SCLValue(type = "ReadGraph -> [Resource] -> [Resource]")
638     public static List<Resource> connectionExtension(ReadGraph graph, List<Resource> rs) throws DatabaseException {
639
640         StructuralResource2 STR = StructuralResource2.getInstance(graph);
641         HashSet<Resource> extension = new HashSet<Resource>(8);
642         for(Resource r : rs) {
643                 if(graph.isInstanceOf(r, STR.Connection)) {
644                         extension.addAll(graph.syncRequest(new ConnectionComponents(r), TransientCacheListener.<Collection<Resource>>instance()));
645                 }
646                 if(graph.isInstanceOf(r, STR.ConnectionJoin)) {
647                         extension.addAll(graph.syncRequest(new ConnectionJoinComponents(r), TransientCacheListener.<Collection<Resource>>instance()));
648                 }
649         }
650
651         HashSet<Resource> components = new HashSet<Resource>(8);
652         for(Resource r : extension) {
653                 components.addAll(graph.sync(new ObjectsWithType(r, STR.Connects, STR.Component)));
654         }
655         
656         if(!extension.isEmpty()) {
657                 ArrayList<Resource> result = new ArrayList<Resource>(rs.size() + extension.size());
658                 result.addAll(rs);
659                 result.addAll(extension);
660                 result.addAll(components);
661                 rs = result;
662         }
663         
664         return rs;
665         
666     }
667
668         @SCLValue(type = "ReadGraph -> Resource -> [Issue]")
669     public static List<Issue> connectionValidator(ReadGraph graph, Resource component) throws DatabaseException {
670         
671                 if(!graph.hasStatement(component)) return Collections.emptyList();
672
673                 ArrayList<Issue> result = new ArrayList<Issue>();
674                 
675                 Layer0 L0 = Layer0.getInstance(graph);
676                 StructuralResource2 sr = StructuralResource2.getInstance(graph);
677
678                 Resource type = graph.getSingleType(component, sr.Component);
679                 
680                 Set<Resource> requiredConnections = new HashSet<Resource>();
681                 for(Resource connectionRelation : graph.sync(new ObjectsWithType(type, L0.ConsistsOf, sr.ConnectionRelation))) {
682                         Boolean required = graph.getPossibleRelatedValue(connectionRelation, sr.ConnectionRelation_connectionRequired, Bindings.BOOLEAN);
683                         if(required != null && required)
684                                 requiredConnections.add(connectionRelation);
685                 }
686                 
687                 Set<Resource> connections = new HashSet<Resource>();
688
689                 for(Statement stm : graph.getStatements(component, sr.IsConnectedTo)) {
690                         connections.add(stm.getPredicate());
691                         connections.addAll(graph.getSuperrelations(stm.getPredicate()));
692                 }
693
694                 for(Resource req : requiredConnections) {
695                         if(!connections.contains(req)) {
696                                 result.add(new StandardIssue(sr.ConnectionConstraint_ErrorIssue, component, req));
697                         }
698                 }
699
700                 return result;
701         
702     }
703
704     @SCLValue(type = "ReadGraph -> Resource -> Variable -> String")
705     public static String connectionIssueDescription(ReadGraph graph, Resource converter, Variable property) throws DatabaseException {
706         List<Resource> contexts = IssueUtils.getContextsForProperty(graph, property);
707         String attributeName = graph.getRelatedValue(contexts.get(1), Layer0.getInstance(graph).HasName);
708         return "'" + attributeName + "' should be connected.";
709     }
710
711     public static class InterfacePathMap extends VariableRead<GraphMap<Map<String,InterfaceResolution>>> {
712
713                 public InterfacePathMap(Variable context) {
714                         super(context);
715                 }
716
717                 @Override
718                 public GraphMap<Map<String,InterfaceResolution>> perform(ReadGraph graph) throws DatabaseException {
719
720                         return new GraphMap<Map<String,InterfaceResolution>>() {
721
722                                 @Override
723                                 Map<String, InterfaceResolution> get(ReadGraph graph, String key) throws DatabaseException {
724                                         
725                                         Variable child = variable.getChild(graph, key);
726                                         
727                                         Map<String,InterfaceResolution> childMap = new THashMap<String,InterfaceResolution>();
728                                         Collection<InterfaceResolution> paths = computeInterfacePaths(graph, child);//child.getPossiblePropertyValue(graph, "proceduralConnectionPointPath");
729                                         if(paths != null) {
730                                                 for(InterfaceResolution r : paths) {
731                                                         childMap.put(r.interfaceName, r);
732                                                 }
733                                         }
734                                         return childMap;
735
736                                 }
737                                 
738                         };
739                         
740                 }
741         
742     }
743     
744     public static String resolveInterfacePath(ReadGraph graph, Variable context, String component, Resource relation) throws DatabaseException {
745         
746         GraphMap<Map<String,InterfaceResolution>> map = graph.syncRequest(new InterfacePathMap(context), TransientCacheListener.<GraphMap<Map<String,InterfaceResolution>>>instance());
747         Map<String,InterfaceResolution> childMap = map.get(graph, component);
748         if(childMap == null) return "";
749
750         PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheListener.<PropertyInfo>instance());
751
752         InterfaceResolution match = childMap.get(info.name);
753         if(match != null) {
754                 String comp = URIStringUtils.escape(component);
755                 Variable newContext = context.getChild(graph, component);
756                 return "/" + comp + resolveInterfacePath(graph, newContext, match.componentName, match.connectionPoint);
757         } else {
758                 return "/" + URIStringUtils.escape(component) + "#" + URIStringUtils.escape(info.name); 
759         }
760                 
761     }
762     
763     public static class InterfaceResolution {
764         
765         public Resource interfaceConnectionPoint;
766         public String interfaceName;
767         public String componentName;
768         public Resource connectionPoint;
769         
770         public InterfaceResolution(Resource interfaceConnectionPoint, String interfaceName, String componentName, Resource connectionPoint) {
771                 this.interfaceConnectionPoint = interfaceConnectionPoint;
772                 this.interfaceName = interfaceName;
773                 this.componentName = componentName;
774                 this.connectionPoint = connectionPoint;
775         }
776         
777     }
778     
779     public static Collection<InterfaceResolution> computeInterfacePaths(ReadGraph graph, Variable variable) throws DatabaseException {
780
781                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
782                 Resource type = variable.getPossibleType(graph);
783                 if(type != null) {
784                         if(graph.isInstanceOf(type, STR.ProceduralComponentType)) {
785                                 ArrayList<InterfaceResolution> result = new ArrayList<InterfaceResolution>();
786                                 List<SubstructureElement> elements = getProceduralDesc(graph, variable); 
787                                 if(elements != null) {
788                                         for(SubstructureElement e : elements) {
789                                                 if(e instanceof org.simantics.structural2.procedural.Connection) {
790                                                         org.simantics.structural2.procedural.Connection conn = (org.simantics.structural2.procedural.Connection)e;
791                                                         Interface inf = null;
792                                                         for(ConnectionPoint cp : conn.connectionPoints) {
793                                                                 if(cp instanceof Interface) {
794                                                                         if(inf != null) throw new DatabaseException("Multiple interfaces referenced in procedural connection.");
795                                                                         inf = (Interface)cp;
796                                                                 }
797                                                         }
798                                                         if(inf != null && conn.connectionPoints.size() > 1) {
799                                                                 Layer0 L0 = Layer0.getInstance(graph);
800                                                                 String cpName = URIStringUtils.escape( graph.<String>getRelatedValue(inf.relation, L0.HasName, Bindings.STRING) );
801                                                                 for(ConnectionPoint cp : conn.connectionPoints) {
802                                                                         if(cp == inf) continue;
803                                                                         Terminal t = (Terminal)cp;
804                                                                         result.add(new InterfaceResolution(inf.relation, cpName, t.component, t.relation));
805                                                                 }
806                                                         }
807                                                 }
808                                         }
809                                 }
810
811                                 return result;
812                                 
813                         }
814
815                         final Collection<InterfaceResolution> interfaces = graph.syncRequest(new DefinedUCInterfaceMap(type));
816                         if(interfaces != null) return interfaces;
817
818                 }
819                 
820                 return BUILTIN_STRUCTURAL_CPS;
821         
822     }
823     
824     static class InterfacePathRequest extends VariableRead<Collection<InterfaceResolution>> {
825
826                 public InterfacePathRequest(Variable variable) {
827                         super(variable);
828                 }
829                 
830                 @Override
831                 public Collection<InterfaceResolution> perform(ReadGraph graph) throws DatabaseException {
832                         return computeInterfacePaths(graph, variable);
833                 }
834         
835     }
836     
837     public static final Collection<InterfaceResolution> BUILTIN_STRUCTURAL_CPS = new ArrayList<InterfaceResolution>();
838
839         @SCLValue(type = "ReadGraph -> Resource -> Variable -> a")
840     public static Object computeExpression(ReadGraph graph, Resource converter, Variable context) throws DatabaseException {
841         return CompileStructuralValueRequest.compileAndEvaluate(graph, context);
842     }
843
844     public static Object computeExpressionInContext(ReadGraph graph, Variable context, final String expression) throws DatabaseException {
845         SCLContext sclContext = SCLContext.getCurrent();
846         Object oldGraph = sclContext.get("graph");
847         try {
848             Function1<Object,Object> exp = graph.syncRequest(new CompileStructuralValueRequest(graph, context) {
849                 protected String getExpressionText(ReadGraph graph) throws DatabaseException {
850                     return expression;
851                 }
852             },
853             TransientCacheListener.instance());
854             sclContext.put("graph", graph);
855             return exp.apply(context);
856         } catch (DatabaseException e) {
857             throw (DatabaseException)e;
858         } catch (Throwable t) {
859             throw new DatabaseException(t);
860         } finally {
861             sclContext.put("graph", oldGraph);
862         }
863     }    
864         
865         static abstract class InterfacePathProperty extends LazyPropertyVariable {
866                 
867                 public InterfacePathProperty(Variable parent) {
868                         super(parent, "proceduralConnectionPointPath", Bindings.STRING_ARRAY);
869                 }
870                 
871                 @Override
872                 public <T> T getValue(ReadGraph graph, Binding binding) throws DatabaseException {
873                         return getValue(graph);
874                 }
875                 
876         }
877         
878         static abstract class GraphMap<Value> {
879                 
880                 abstract Value get(ReadGraph graph, String key) throws DatabaseException;
881                 
882         }
883     
884 }