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