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