1 package org.simantics.modeling.scl.ontologymodule;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.HashMap;
9 import java.util.function.Consumer;
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;
52 import gnu.trove.map.hash.THashMap;
53 import gnu.trove.procedure.TObjectProcedure;
55 public class OntologyModule extends LazyModule {
56 private static final Logger LOGGER = LoggerFactory.getLogger(OntologyModule.class);
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");
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;
71 public OntologyModule(ReadGraph graph, String moduleName) throws DatabaseException {
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>>() {
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) {
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, "");
89 protected String getRootModuleName() {
90 return SCL_TYPES_NAME;
95 for(ImportDeclaration decl : pair.first.imports)
96 importDeclarations.add(decl.hidden());
97 this.environment = pair.second;
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 = "";
108 public String getDefaultLocalName() {
109 return defaultLocalName;
113 public List<ImportDeclaration> getDependencies() {
114 return importDeclarations;
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;
124 public String toString() {
125 return "JustResource(" + resource + ")";
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;
136 public String toString() {
137 return "ResourceAndSuffix(" + resource + ", " + suffix + ")";
141 private ResourceSearchResult getResourceOrSuffixedResource(String name) {
142 Map<String,Resource> localMap = childMaps.get(ontology);
145 Resource parent = ontology;
147 int p = name.indexOf('.');
150 String localName = name.substring(0, p);
151 parent = localMap.get(localName);
154 name = name.substring(p+1);
157 localMap = getLocalMap(parent);
161 Resource child = localMap.get(name);
163 return new JustResource(child);
165 return new ResourceAndSuffix(parent, name);
168 private Resource getResource(String name) {
169 ResourceSearchResult searchResult = getResourceOrSuffixedResource(name);
170 if(searchResult instanceof JustResource)
171 return ((JustResource)searchResult).resource;
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))
181 localMap = createLocalMap(parent);
182 childMaps.put(parent, localMap);
187 private static Map<String, Resource> createLocalMap(final Resource parent) {
188 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
190 return createLocalMap(graph, parent);
193 return Simantics.getSession().syncRequest(new Read<Map<String, Resource>>() {
195 public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
196 return createLocalMap(graph, parent);
199 } catch(DatabaseException e) {
205 private static Map<String, Resource> createLocalMap(ReadGraph graph, Resource parent) {
207 return graph.syncRequest(new UnescapedChildMapOfResource(parent));
208 } catch (DatabaseException e) {
215 private static interface ResourceFunctionGenerator {
216 SCLValue createValue(Name name, Resource resource, Environment environment);
219 private static class RelatedValueMacroRule implements MacroRule {
220 private final Resource relation;
221 private final SCLRelationInfo relationInfo;
222 private final boolean optionalValue;
224 public RelatedValueMacroRule(Resource relation, SCLRelationInfo relationInfo, boolean optionalValue) {
225 this.relation = relation;
226 this.relationInfo = relationInfo;
227 this.optionalValue = optionalValue;
230 private Expression applyWithSubject(SimplificationContext context, Type subjectType, Expression evidence, Expression subject) {
231 if(Types.equals(subjectType, RESOURCE))
233 Locations.NO_LOCATION,
235 context.getConstant(Name.create(DB_MODULE, optionalValue ? "possibleRelatedValue2" : "relatedValue2"), relationInfo.rangeType),
237 new EExternalConstant(relation, RESOURCE));
238 else if(Types.equals(subjectType, VARIABLE))
240 Locations.NO_LOCATION,
242 context.getConstant(Name.create(DB_MODULE, optionalValue ? "untypedPossiblePropertyValue" : "untypedPropertyValue"), relationInfo.rangeType),
244 new ELiteral(new StringConstant(relationInfo.name)));
247 Locations.NO_LOCATION,
249 context.getConstant(Name.create(DB_MODULE, optionalValue ? "genericPossibleRelatedValue" : "genericRelatedValue"), subjectType, relationInfo.rangeType),
252 new EExternalConstant(relation, RESOURCE));
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)));
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;
267 apply.set(valueReplacement, Arrays.copyOfRange(apply.parameters, 2, apply.parameters.length));
272 LOGGER.error("Application of relation following functions should have at least one parameter (the evidence of Browsable).");
278 private final static HashMap<String, ResourceFunctionGenerator> VALUE_GENERATOR_MAP = new HashMap<>();
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)
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));
291 VALUE_GENERATOR_MAP.put("possibleValue", (name, resource, environment) -> {
292 SCLRelationInfo relationInfo = SCLRelationInfoRequest.getRelationInfo(resource, environment);
293 if(relationInfo == null)
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));
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);
314 else if(searchResult instanceof ResourceAndSuffix){
315 ResourceAndSuffix resourceAndSuffix = (ResourceAndSuffix)searchResult;
316 ResourceFunctionGenerator generator = VALUE_GENERATOR_MAP.get(resourceAndSuffix.suffix);
317 if(generator == null)
320 return generator.createValue(Name.create(getName(), name), resourceAndSuffix.resource, environment);
327 protected SCLRelation createRelation(String name) {
328 final Resource resource = getResource(name);
331 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
333 return createRelation(graph, resource);
336 return Simantics.getSession().syncRequest(new Read<SCLRelation>() {
338 public SCLRelation perform(ReadGraph graph) throws DatabaseException {
339 return createRelation(graph, resource);
342 } catch(DatabaseException e) {
348 public static SCLRelation createRelation(ReadGraph graph, Resource relation) {
350 Layer0 L0 = Layer0.getInstance(graph);
351 if(!graph.isInstanceOf(relation, L0.Relation))
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);
359 Resource inverseRelation = graph.getPossibleInverse(relation);
360 return new GraphRelation(relation, getSelectivity(graph, relation),
361 inverseRelation, getSelectivity(graph, inverseRelation));
362 } catch(DatabaseException e) {
369 protected SCLEntityType createEntityType(String name) {
370 final Resource resource = getResource(name);
373 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
375 return createEntityType(graph, resource);
378 return Simantics.getSession().syncRequest(new Read<SCLEntityType>() {
380 public SCLEntityType perform(ReadGraph graph) throws DatabaseException {
381 return createEntityType(graph, resource);
384 } catch(DatabaseException e) {
390 private SCLEntityType createEntityType(ReadGraph graph, Resource type) {
392 Layer0 L0 = Layer0.getInstance(graph);
393 if(!graph.isInstanceOf(type, L0.Type))
395 return new GraphEntityType(graph, type);
396 } catch(DatabaseException e) {
402 private static double getSelectivity(ReadGraph graph, Resource relation) throws DatabaseException {
404 return Double.POSITIVE_INFINITY;
405 Layer0 L0 = Layer0.getInstance(graph);
406 if(graph.isInstanceOf(relation, L0.FunctionalRelation))
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)
417 Resource range = graph.getPossibleObject(relation, L0.HasRange);
419 for(Resource valueTypeLiteral : graph.getAssertedObjects(range, L0.HasValueType)) {
420 valueType = parseValueType((String)graph.getValue(valueTypeLiteral, Bindings.STRING));
421 if(valueType != null)
428 private static Type parseValueType(String valueTypeString) {
429 if(valueTypeString == null)
432 return Types.parseType(valueTypeString);
433 } catch (SCLTypeParseException e) {
440 public void findValuesForPrefix(String prefix,
441 NamespaceFilter filter,
442 TObjectProcedure<SCLValue> proc) {
443 Map<String,Resource> localMap = childMaps.get(ontology);
446 String namePrefix = "";
448 int p = prefix.indexOf('.');
451 String localName = prefix.substring(0, p);
452 Resource newParent = localMap.get(localName);
453 if(newParent == null)
455 prefix = prefix.substring(p+1);
456 namePrefix = namePrefix + localName + ".";
459 localMap = getLocalMap(newParent);
463 for(String name : localMap.keySet())
464 if(name.startsWith(prefix) && filter.isValueIncluded(name))
465 proc.execute(getValue(namePrefix+name));
469 public void findValuesForPrefix(String prefix, NamespaceFilter filter, Consumer<SCLValue> consumer) {
470 Map<String,Resource> localMap = childMaps.get(ontology);
473 String namePrefix = "";
475 int p = prefix.indexOf('.');
478 String localName = prefix.substring(0, p);
479 Resource newParent = localMap.get(localName);
480 if(newParent == null)
482 prefix = prefix.substring(p+1);
483 namePrefix = namePrefix + localName + ".";
486 localMap = getLocalMap(newParent);
490 for(String name : localMap.keySet())
491 if(name.startsWith(prefix) && filter.isValueIncluded(name))
492 consumer.accept(getValue(namePrefix+name));
496 public void findTypesForPrefix(String prefix, NamespaceFilter instance, Consumer<TCon> consumer) {
501 public void dispose() {
509 public String toString() {
510 return new StringBuilder().append("OntologyModule ").append(getName()).toString();
514 public ClassLoader getParentClassLoader() {
515 return getClass().getClassLoader();