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