1 package org.simantics.modeling.scl.ontologymodule;
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.HashMap;
8 import java.util.function.Consumer;
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;
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 Collection<ImportDeclaration> DEPENDENCIES = Arrays.asList(
62 new ImportDeclaration(DB_MODULE, null),
63 new ImportDeclaration(VARIABLE_MODULE, null)
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");
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;
75 public OntologyModule(ReadGraph graph, String moduleName) throws DatabaseException {
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>>() {
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) {
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, "");
93 protected String getRootModuleName() {
94 return SCL_TYPES_NAME;
99 this.importDeclarations = pair.first.imports;
100 this.environment = pair.second;
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 = "";
111 public String getDefaultLocalName() {
112 return defaultLocalName;
116 public List<ImportDeclaration> getDependencies() {
117 return importDeclarations;
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;
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;
136 private ResourceSearchResult getResourceOrSuffixedResource(String name) {
137 Map<String,Resource> localMap = childMaps.get(ontology);
140 Resource parent = ontology;
142 int p = name.indexOf('.');
145 String localName = name.substring(0, p);
146 parent = localMap.get(localName);
149 name = name.substring(p+1);
152 localMap = getLocalMap(parent);
156 Resource child = localMap.get(name);
158 return new JustResource(child);
160 return new ResourceAndSuffix(parent, name);
163 private Resource getResource(String name) {
164 ResourceSearchResult searchResult = getResourceOrSuffixedResource(name);
165 if(searchResult instanceof JustResource)
166 return ((JustResource)searchResult).resource;
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))
176 localMap = createLocalMap(parent);
177 childMaps.put(parent, localMap);
182 private static Map<String, Resource> createLocalMap(final Resource parent) {
183 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
185 return createLocalMap(graph, parent);
188 return Simantics.getSession().syncRequest(new Read<Map<String, Resource>>() {
190 public Map<String, Resource> perform(ReadGraph graph) throws DatabaseException {
191 return createLocalMap(graph, parent);
194 } catch(DatabaseException e) {
200 private static Map<String, Resource> createLocalMap(ReadGraph graph, Resource parent) {
202 return graph.syncRequest(new UnescapedChildMapOfResource(parent));
203 } catch (DatabaseException e) {
210 private static interface ResourceFunctionGenerator {
211 SCLValue createValue(Name name, Resource resource, Environment environment);
214 private static class RelatedValueMacroRule implements MacroRule {
215 private final Resource relation;
216 private final SCLRelationInfo relationInfo;
217 private final boolean optionalValue;
219 public RelatedValueMacroRule(Resource relation, SCLRelationInfo relationInfo, boolean optionalValue) {
220 this.relation = relation;
221 this.relationInfo = relationInfo;
222 this.optionalValue = optionalValue;
225 private Expression applyWithSubject(SimplificationContext context, Type subjectType, Expression evidence, Expression subject) {
226 if(Types.equals(subjectType, RESOURCE))
228 Locations.NO_LOCATION,
230 context.getConstant(Name.create(DB_MODULE, optionalValue ? "possibleRelatedValue2" : "relatedValue2"), relationInfo.rangeType),
232 new EExternalConstant(relation, RESOURCE));
233 else if(Types.equals(subjectType, VARIABLE))
235 Locations.NO_LOCATION,
237 context.getConstant(Name.create(DB_MODULE, optionalValue ? "untypedPossiblePropertyValue" : "untypedPropertyValue"), relationInfo.rangeType),
239 new ELiteral(new StringConstant(relationInfo.name)));
242 Locations.NO_LOCATION,
244 context.getConstant(Name.create(DB_MODULE, optionalValue ? "genericPossibleRelatedValue" : "genericRelatedValue"), subjectType, relationInfo.rangeType),
247 new EExternalConstant(relation, RESOURCE));
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)));
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;
262 apply.set(valueReplacement, Arrays.copyOfRange(apply.parameters, 2, apply.parameters.length));
267 LOGGER.error("Application of relation following functions should have at least one parameter (the evidence of Browsable).");
273 private final static HashMap<String, ResourceFunctionGenerator> VALUE_GENERATOR_MAP = new HashMap<>();
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)
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));
286 VALUE_GENERATOR_MAP.put("possibleValue", (name, resource, environment) -> {
287 SCLRelationInfo relationInfo = SCLRelationInfoRequest.getRelationInfo(resource, environment);
288 if(relationInfo == null)
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));
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);
309 else if(searchResult instanceof ResourceAndSuffix){
310 ResourceAndSuffix resourceAndSuffix = (ResourceAndSuffix)searchResult;
311 ResourceFunctionGenerator generator = VALUE_GENERATOR_MAP.get(resourceAndSuffix.suffix);
312 if(generator == null)
315 return generator.createValue(Name.create(getName(), name), resourceAndSuffix.resource, environment);
322 protected SCLRelation createRelation(String name) {
323 final Resource resource = getResource(name);
326 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
328 return createRelation(graph, resource);
331 return Simantics.getSession().syncRequest(new Read<SCLRelation>() {
333 public SCLRelation perform(ReadGraph graph) throws DatabaseException {
334 return createRelation(graph, resource);
337 } catch(DatabaseException e) {
343 public static SCLRelation createRelation(ReadGraph graph, Resource relation) {
345 Layer0 L0 = Layer0.getInstance(graph);
346 if(!graph.isInstanceOf(relation, L0.Relation))
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);
354 Resource inverseRelation = graph.getPossibleInverse(relation);
355 return new GraphRelation(relation, getSelectivity(graph, relation),
356 inverseRelation, getSelectivity(graph, inverseRelation));
357 } catch(DatabaseException e) {
364 protected SCLEntityType createEntityType(String name) {
365 final Resource resource = getResource(name);
368 ReadGraph graph = (ReadGraph)SCLContext.getCurrent().get("graph");
370 return createEntityType(graph, resource);
373 return Simantics.getSession().syncRequest(new Read<SCLEntityType>() {
375 public SCLEntityType perform(ReadGraph graph) throws DatabaseException {
376 return createEntityType(graph, resource);
379 } catch(DatabaseException e) {
385 private SCLEntityType createEntityType(ReadGraph graph, Resource type) {
387 Layer0 L0 = Layer0.getInstance(graph);
388 if(!graph.isInstanceOf(type, L0.Type))
390 return new GraphEntityType(graph, type);
391 } catch(DatabaseException e) {
397 private static double getSelectivity(ReadGraph graph, Resource relation) throws DatabaseException {
399 return Double.POSITIVE_INFINITY;
400 Layer0 L0 = Layer0.getInstance(graph);
401 if(graph.isInstanceOf(relation, L0.FunctionalRelation))
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)
412 Resource range = graph.getPossibleObject(relation, L0.HasRange);
414 for(Resource valueTypeLiteral : graph.getAssertedObjects(range, L0.HasValueType)) {
415 valueType = parseValueType((String)graph.getValue(valueTypeLiteral, Bindings.STRING));
416 if(valueType != null)
423 private static Type parseValueType(String valueTypeString) {
424 if(valueTypeString == null)
427 return Types.parseType(valueTypeString);
428 } catch (SCLTypeParseException e) {
435 public void findValuesForPrefix(String prefix,
436 NamespaceFilter filter,
437 TObjectProcedure<SCLValue> proc) {
438 Map<String,Resource> localMap = childMaps.get(ontology);
441 String namePrefix = "";
443 int p = prefix.indexOf('.');
446 String localName = prefix.substring(0, p);
447 Resource newParent = localMap.get(localName);
448 if(newParent == null)
450 prefix = prefix.substring(p+1);
451 namePrefix = namePrefix + localName + ".";
454 localMap = getLocalMap(newParent);
458 for(String name : localMap.keySet())
459 if(name.startsWith(prefix) && filter.isValueIncluded(name))
460 proc.execute(getValue(namePrefix+name));
464 public void findValuesForPrefix(String prefix, NamespaceFilter filter, Consumer<SCLValue> consumer) {
465 Map<String,Resource> localMap = childMaps.get(ontology);
468 String namePrefix = "";
470 int p = prefix.indexOf('.');
473 String localName = prefix.substring(0, p);
474 Resource newParent = localMap.get(localName);
475 if(newParent == null)
477 prefix = prefix.substring(p+1);
478 namePrefix = namePrefix + localName + ".";
481 localMap = getLocalMap(newParent);
485 for(String name : localMap.keySet())
486 if(name.startsWith(prefix) && filter.isValueIncluded(name))
487 consumer.accept(getValue(namePrefix+name));
491 public void findTypesForPrefix(String prefix, NamespaceFilter instance, Consumer<TCon> consumer) {
496 public void dispose() {
504 public String toString() {
505 return new StringBuilder().append("OntologyModule ").append(getName()).toString();
509 public ClassLoader getParentClassLoader() {
510 return getClass().getClassLoader();