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