]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/scl/ontologymodule/OntologyModule.java
Use proper environment to resolve SCL types in ontology module
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / scl / ontologymodule / OntologyModule.java
1 package org.simantics.modeling.scl.ontologymodule;
2
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.function.Consumer;
9
10 import org.simantics.Simantics;
11 import org.simantics.databoard.Bindings;
12 import org.simantics.db.ReadGraph;
13 import org.simantics.db.Resource;
14 import org.simantics.db.common.request.IndexRoot;
15 import org.simantics.db.common.uri.UnescapedChildMapOfResource;
16 import org.simantics.db.exception.DatabaseException;
17 import org.simantics.db.layer0.util.EnvironmentRequest;
18 import org.simantics.db.layer0.util.RuntimeEnvironmentRequest;
19 import org.simantics.db.request.Read;
20 import org.simantics.layer0.Layer0;
21 import org.simantics.scl.compiler.common.names.Name;
22 import org.simantics.scl.compiler.constants.StringConstant;
23 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
24 import org.simantics.scl.compiler.elaboration.expressions.EApply;
25 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
26 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
27 import org.simantics.scl.compiler.elaboration.expressions.ESimpleLambda;
28 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
29 import org.simantics.scl.compiler.elaboration.expressions.Expression;
30 import org.simantics.scl.compiler.elaboration.expressions.Variable;
31 import org.simantics.scl.compiler.elaboration.macros.MacroRule;
32 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
33 import org.simantics.scl.compiler.elaboration.relations.SCLEntityType;
34 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
35 import org.simantics.scl.compiler.environment.Environment;
36 import org.simantics.scl.compiler.environment.filter.NamespaceFilter;
37 import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
38 import org.simantics.scl.compiler.errors.Locations;
39 import org.simantics.scl.compiler.module.ImportDeclaration;
40 import org.simantics.scl.compiler.module.LazyModule;
41 import org.simantics.scl.compiler.types.TCon;
42 import org.simantics.scl.compiler.types.TVar;
43 import org.simantics.scl.compiler.types.Type;
44 import org.simantics.scl.compiler.types.Types;
45 import org.simantics.scl.compiler.types.exceptions.SCLTypeParseException;
46 import org.simantics.scl.compiler.types.kinds.Kinds;
47 import org.simantics.scl.runtime.SCLContext;
48 import org.simantics.utils.datastructures.Pair;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import gnu.trove.map.hash.THashMap;
53 import gnu.trove.procedure.TObjectProcedure;
54
55 public class OntologyModule extends LazyModule {
56     private static final Logger LOGGER = LoggerFactory.getLogger(OntologyModule.class);
57     
58     public static final String SCL_TYPES_NAME = "SCLTypes";
59     private static final String DB_MODULE = "Simantics/DB";
60     private static final String VARIABLE_MODULE = "Simantics/Variable";
61     private static final Collection<ImportDeclaration> DEPENDENCIES = Arrays.asList(
62             new ImportDeclaration(DB_MODULE, null),
63             new ImportDeclaration(VARIABLE_MODULE, null)
64             );
65     private static final TCon RESOURCE = Types.con(DB_MODULE, "Resource");
66     private static final TCon BROWSABLE = Types.con(DB_MODULE, "Browsable");
67     private static final TCon VARIABLE = Types.con(VARIABLE_MODULE, "Variable");
68     
69     private Resource ontology;
70     private String defaultLocalName;
71     private THashMap<Resource,Map<String,Resource>> childMaps = new THashMap<Resource,Map<String,Resource>>();
72     private List<ImportDeclaration> importDeclarations;
73     private Environment environment;
74     
75     public OntologyModule(ReadGraph graph, String moduleName) throws DatabaseException {
76         super(moduleName);
77         ontology = graph.getResource(moduleName);
78         readDefaultLocalName(graph);
79         childMaps.put(ontology, createLocalMap(graph, ontology));
80         Pair<EnvironmentSpecification, Environment> pair = graph.syncRequest(new Read<Pair<EnvironmentSpecification, Environment>>() {
81             @Override
82             public Pair<EnvironmentSpecification, Environment> perform(ReadGraph graph) throws DatabaseException {
83                 Resource indexRoot = graph.syncRequest(new IndexRoot(ontology));
84                 return graph.syncRequest(new EnvironmentRequest(indexRoot) {
85                     @Override
86                     protected void fillEnvironmentSpecification(EnvironmentSpecification environmentSpecification) {
87                         /*if(!moduleName.equals("http://www.simantics.org/Layer0-1.1")) { // Prevent cyclic dependencies
88                             environmentSpecification.importModule(DB_MODULE, "");
89                             environmentSpecification.importModule(VARIABLE_MODULE, "");
90                         }*/
91                     }
92                     @Override
93                     protected String getRootModuleName() {
94                         return SCL_TYPES_NAME;
95                     }
96                 });
97             }
98         });
99         this.importDeclarations = pair.first.imports;
100         this.environment = pair.second;
101     }
102     
103     private void readDefaultLocalName(ReadGraph graph) throws DatabaseException {
104         Layer0 L0 = Layer0.getInstance(graph);
105         defaultLocalName = graph.getPossibleRelatedValue(ontology, L0.Ontology_defaultLocalName);
106         if(defaultLocalName == null)
107             defaultLocalName = "";
108     }
109
110         @Override
111     public String getDefaultLocalName() {
112         return defaultLocalName;
113     }
114
115     @Override
116     public List<ImportDeclaration> getDependencies() {
117         return importDeclarations;
118     }
119     
120     private static interface ResourceSearchResult {}
121     private static class JustResource implements ResourceSearchResult {
122         public final Resource resource;
123         public JustResource(Resource resource) {
124             this.resource = resource;
125         }   
126     }
127     private static class ResourceAndSuffix implements ResourceSearchResult {
128         public final Resource resource;
129         public final String suffix;
130         public ResourceAndSuffix(Resource resource, String suffix) {
131             this.resource = resource;
132             this.suffix = suffix;
133         }  
134     }
135     
136     private ResourceSearchResult getResourceOrSuffixedResource(String name) {
137         Map<String,Resource> localMap = childMaps.get(ontology); 
138         if(localMap == null)
139             return null;
140         Resource parent = ontology;
141         while(true) {
142             int p = name.indexOf('.');
143             if(p < 0)
144                 break;
145             String localName = name.substring(0, p);
146             parent = localMap.get(localName);
147             if(parent == null)
148                 return null;
149             name = name.substring(p+1);
150             
151             // Get new local map
152             localMap = getLocalMap(parent);
153             if(localMap == null)
154                 return null;
155         }
156         Resource child = localMap.get(name);
157         if(child != null)
158             return new JustResource(child);
159         else
160             return new ResourceAndSuffix(parent, name);
161     }
162     
163     private Resource getResource(String name) {
164         ResourceSearchResult searchResult = getResourceOrSuffixedResource(name);
165         if(searchResult instanceof JustResource)
166             return ((JustResource)searchResult).resource;
167         else
168             return null;
169     }
170     
171     private Map<String, Resource> getLocalMap(Resource parent) {
172         Map<String, Resource> localMap = childMaps.get(parent);
173         if(localMap == null) {
174             if(childMaps.contains(parent))
175                 return null;
176             localMap = createLocalMap(parent);
177             childMaps.put(parent, localMap);
178         }
179         return localMap;
180     }
181
182     private static Map<String, Resource> createLocalMap(final Resource parent) {
183         ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
184         if(graph != null)
185             return createLocalMap(graph, parent);
186         else
187             try {
188                 return Simantics.getSession().syncRequest(new Read<Map<String, Resource>>() {
189                     @Override
190                     public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
191                         return createLocalMap(graph, parent);
192                     }
193                 });
194             } catch(DatabaseException e) {
195                 e.printStackTrace();
196                 return null;
197             }   
198     }
199
200     private static Map<String, Resource> createLocalMap(ReadGraph graph, Resource parent) {
201         try {
202             return graph.syncRequest(new UnescapedChildMapOfResource(parent));
203         } catch (DatabaseException e) {
204             e.printStackTrace();
205             return null;
206         }
207     }
208     
209     @FunctionalInterface
210     private static interface ResourceFunctionGenerator {
211         SCLValue createValue(Name name, Resource resource, Environment environment);
212     }
213     
214     private static class RelatedValueMacroRule implements MacroRule {
215         private final Resource relation;
216         private final SCLRelationInfo relationInfo;
217         private final boolean optionalValue;
218         
219         public RelatedValueMacroRule(Resource relation, SCLRelationInfo relationInfo, boolean optionalValue) {
220             this.relation = relation;
221             this.relationInfo = relationInfo;
222             this.optionalValue = optionalValue;
223         }
224
225         private Expression applyWithSubject(SimplificationContext context, Type subjectType, Expression evidence, Expression subject) {
226             if(Types.equals(subjectType, RESOURCE))
227                 return new EApply(
228                         Locations.NO_LOCATION,
229                         Types.READ_GRAPH,
230                         context.getConstant(Name.create(DB_MODULE, optionalValue ? "possibleRelatedValue2" : "relatedValue2"), relationInfo.rangeType),
231                         subject,
232                         new EExternalConstant(relation, RESOURCE));
233             else if(Types.equals(subjectType, VARIABLE))
234                 return new EApply(
235                         Locations.NO_LOCATION,
236                         Types.READ_GRAPH,
237                         context.getConstant(Name.create(DB_MODULE, optionalValue ? "untypedPossiblePropertyValue" : "untypedPropertyValue"), relationInfo.rangeType),
238                         subject,
239                         new ELiteral(new StringConstant(relationInfo.name)));
240             else
241                 return new EApply(
242                         Locations.NO_LOCATION,
243                         Types.READ_GRAPH,
244                         context.getConstant(Name.create(DB_MODULE, optionalValue ? "genericPossibleRelatedValue" : "genericRelatedValue"), subjectType, relationInfo.rangeType),
245                         evidence,
246                         subject,
247                         new EExternalConstant(relation, RESOURCE));
248         }
249         
250         @Override
251         public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) {
252             Type subjectType = typeParameters[0];
253             if(apply.parameters.length == 1) {
254                 Variable subject = new Variable("subject", subjectType);
255                 return new ESimpleLambda(subject, applyWithSubject(context, subjectType, apply.parameters[0], new EVariable(subject)));
256             }
257             else if(apply.parameters.length >= 2) {
258                 Expression valueReplacement = applyWithSubject(context, subjectType, apply.parameters[0], apply.parameters[1]);
259                 if(apply.parameters.length == 2)
260                     return valueReplacement;
261                 else {
262                     apply.set(valueReplacement, Arrays.copyOfRange(apply.parameters, 2, apply.parameters.length));
263                     return apply;
264                 }
265             }
266             else {
267                 LOGGER.error("Application of relation following functions should have at least one parameter (the evidence of Browsable).");
268                 return null;
269             }
270         }
271     }
272     
273     private final static HashMap<String, ResourceFunctionGenerator> VALUE_GENERATOR_MAP = new HashMap<>();
274     static {
275         TVar A = Types.var(Kinds.STAR);
276         VALUE_GENERATOR_MAP.put("value", (name, resource, environment) -> {
277             SCLRelationInfo relationInfo = SCLRelationInfoRequest.getRelationInfo(resource, environment);
278             if(relationInfo == null)
279                 return null;
280             
281             SCLValue value = new SCLValue(name);
282             value.setType(Types.forAll(A, Types.function(Types.pred(BROWSABLE, A), Types.functionE(A, Types.READ_GRAPH, relationInfo.rangeType))));
283             value.setMacroRule(new RelatedValueMacroRule(resource, relationInfo, false));
284             return value;
285         });
286         VALUE_GENERATOR_MAP.put("possibleValue", (name, resource, environment) -> {
287             SCLRelationInfo relationInfo = SCLRelationInfoRequest.getRelationInfo(resource, environment);
288             if(relationInfo == null)
289                 return null;
290             
291             SCLValue value = new SCLValue(name);
292             value.setType(Types.forAll(A, Types.function(Types.pred(BROWSABLE, A), Types.functionE(A, Types.READ_GRAPH, Types.apply(Types.MAYBE, relationInfo.rangeType)))));
293             value.setMacroRule(new RelatedValueMacroRule(resource, relationInfo, true));
294             return value;
295         });
296     }
297    
298     @Override
299     protected SCLValue createValue(String name) {
300         ResourceSearchResult searchResult = getResourceOrSuffixedResource(name);
301         if(searchResult instanceof JustResource) {
302             Resource resource = ((JustResource)searchResult).resource;
303             SCLValue value = new SCLValue(Name.create(getName(), name));
304             value.setType(RESOURCE);
305             value.setExpression(new EExternalConstant(resource, RESOURCE));
306             value.setInlineInSimplification(true);
307             return value;        
308         }
309         else if(searchResult instanceof ResourceAndSuffix){
310             ResourceAndSuffix resourceAndSuffix = (ResourceAndSuffix)searchResult;
311             ResourceFunctionGenerator generator = VALUE_GENERATOR_MAP.get(resourceAndSuffix.suffix);
312             if(generator == null)
313                 return null;
314             else
315                 return generator.createValue(Name.create(getName(), name), resourceAndSuffix.resource, environment);
316         }
317         else
318             return null;
319     }
320     
321     @Override
322     protected SCLRelation createRelation(String name) {
323         final Resource resource = getResource(name);
324         if(resource == null)
325             return null;
326         ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
327         if(graph != null)
328             return createRelation(graph, resource);
329         else
330             try {
331                 return Simantics.getSession().syncRequest(new Read<SCLRelation>() {
332                     @Override
333                     public SCLRelation perform(ReadGraph graph) throws DatabaseException {
334                         return createRelation(graph, resource);
335                     }
336                 });
337             } catch(DatabaseException e) {
338                 e.printStackTrace();
339                 return null;
340             }   
341     }
342     
343     public static SCLRelation createRelation(ReadGraph graph, Resource relation) {
344         try {
345             Layer0 L0 = Layer0.getInstance(graph);
346             if(!graph.isInstanceOf(relation, L0.Relation))
347                 return null;
348             if(graph.isInstanceOf(relation, L0.PropertyRelation) && graph.isInstanceOf(relation, L0.FunctionalRelation)) {
349                 Type valueType = getValueType(graph, relation);
350                 if(valueType != null)
351                     return new GraphPropertyRelation(relation, valueType);
352             }
353             
354             Resource inverseRelation = graph.getPossibleInverse(relation);
355             return new GraphRelation(relation, getSelectivity(graph, relation),
356                     inverseRelation, getSelectivity(graph, inverseRelation));
357         } catch(DatabaseException e) {
358             e.printStackTrace();
359             return null;
360         }
361     }
362     
363     @Override
364     protected SCLEntityType createEntityType(String name) {
365         final Resource resource = getResource(name);
366         if(resource == null)
367             return null;
368         ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
369         if(graph != null)
370             return createEntityType(graph, resource);
371         else
372             try {
373                 return Simantics.getSession().syncRequest(new Read<SCLEntityType>() {
374                     @Override
375                     public SCLEntityType perform(ReadGraph graph) throws DatabaseException {
376                         return createEntityType(graph, resource);
377                     }
378                 });
379             } catch(DatabaseException e) {
380                 e.printStackTrace();
381                 return null;
382             }   
383     }
384     
385     private SCLEntityType createEntityType(ReadGraph graph, Resource type) {
386         try {
387             Layer0 L0 = Layer0.getInstance(graph);
388             if(!graph.isInstanceOf(type, L0.Type))
389                 return null;
390             return new GraphEntityType(graph, type);
391         } catch(DatabaseException e) {
392             e.printStackTrace();
393             return null;
394         }
395     }
396     
397     private static double getSelectivity(ReadGraph graph, Resource relation) throws DatabaseException {
398         if(relation == null)
399             return Double.POSITIVE_INFINITY;
400         Layer0 L0 = Layer0.getInstance(graph);
401         if(graph.isInstanceOf(relation, L0.FunctionalRelation))
402             return 1.0;
403         else
404             return 10.0;
405     }
406
407     private static Type getValueType(ReadGraph graph, Resource relation) throws DatabaseException {
408         Layer0 L0 = Layer0.getInstance(graph);
409         Type valueType = parseValueType((String)graph.getPossibleRelatedValue(relation, L0.RequiresValueType, Bindings.STRING));
410         if(valueType != null)
411             return valueType;
412         Resource range = graph.getPossibleObject(relation, L0.HasRange);
413         if(range != null) {
414             for(Resource valueTypeLiteral : graph.getAssertedObjects(range, L0.HasValueType)) {
415                 valueType = parseValueType((String)graph.getValue(valueTypeLiteral, Bindings.STRING));
416                 if(valueType != null)
417                     return valueType;
418             }
419         }
420         return null;
421     }
422     
423     private static Type parseValueType(String valueTypeString) {
424         if(valueTypeString == null)
425             return null;
426         try {
427             return Types.parseType(valueTypeString);
428         } catch (SCLTypeParseException e) {
429             e.printStackTrace();
430             return null;
431         }
432     }
433     
434     @Override
435     public void findValuesForPrefix(String prefix,
436             NamespaceFilter filter,
437             TObjectProcedure<SCLValue> proc) {
438         Map<String,Resource> localMap = childMaps.get(ontology); 
439         if(localMap == null)
440             return;
441         String namePrefix = "";
442         while(true) {
443             int p = prefix.indexOf('.');
444             if(p < 0)
445                 break;
446             String localName = prefix.substring(0, p);
447             Resource newParent = localMap.get(localName);
448             if(newParent == null)
449                 return;
450             prefix = prefix.substring(p+1);
451             namePrefix = namePrefix + localName + ".";
452             
453             // Get new local map
454             localMap = getLocalMap(newParent);
455             if(localMap == null)
456                 return;
457         }
458         for(String name : localMap.keySet())
459             if(name.startsWith(prefix) && filter.isValueIncluded(name))
460                 proc.execute(getValue(namePrefix+name));
461     }
462     
463     @Override
464     public void findValuesForPrefix(String prefix, NamespaceFilter filter, Consumer<SCLValue> consumer) {
465         Map<String,Resource> localMap = childMaps.get(ontology); 
466         if(localMap == null)
467             return;
468         String namePrefix = "";
469         while(true) {
470             int p = prefix.indexOf('.');
471             if(p < 0)
472                 break;
473             String localName = prefix.substring(0, p);
474             Resource newParent = localMap.get(localName);
475             if(newParent == null)
476                 return;
477             prefix = prefix.substring(p+1);
478             namePrefix = namePrefix + localName + ".";
479             
480             // Get new local map
481             localMap = getLocalMap(newParent);
482             if(localMap == null)
483                 return;
484         }
485         for(String name : localMap.keySet())
486             if(name.startsWith(prefix) && filter.isValueIncluded(name))
487                 consumer.accept(getValue(namePrefix+name));
488     }
489
490     @Override
491     public void findTypesForPrefix(String prefix, NamespaceFilter instance, Consumer<TCon> consumer) {
492         
493     }
494
495     @Override
496     public void dispose() {
497         childMaps.clear();
498         childMaps = null;
499         ontology = null;
500         environment = null;
501     }
502     
503     @Override
504     public String toString() {
505         return new StringBuilder().append("OntologyModule ").append(getName()).toString();
506     }
507
508     @Override
509     public ClassLoader getParentClassLoader() {
510         return getClass().getClassLoader();
511     }
512 }