package org.simantics.modeling.scl; import org.simantics.db.Resource; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.elaboration.chr.plan.PlanContext; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant; import org.simantics.scl.compiler.elaboration.expressions.EVariable; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.expressions.Variable; import org.simantics.scl.compiler.elaboration.query.compilation.EnforcingContext; import org.simantics.scl.compiler.elaboration.query.compilation.QueryCompilationContext; import org.simantics.scl.compiler.elaboration.relations.SCLRelation; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter; import org.simantics.scl.compiler.types.TVar; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; public class GraphRelation implements SCLRelation { Resource relation; double relationSelectivity; Resource inverseRelation; double inverseRelationSelectivity; public GraphRelation(Resource relation, double relationSelectivity, Resource inverseRelation, double inverseRelationSelectivity) { this.relation = relation; this.relationSelectivity = relationSelectivity; this.inverseRelation = inverseRelation; this.inverseRelationSelectivity = inverseRelationSelectivity; } @Override public TVar[] getTypeVariables() { return TVar.EMPTY_ARRAY; } private static final Type[] PARAMETER_TYPES = new Type[] { Types.RESOURCE, Types.RESOURCE }; @Override public Type[] getParameterTypes() { return PARAMETER_TYPES; } @Override public double getSelectivity(int boundVariables) { switch(boundVariables) { case FF: return Double.POSITIVE_INFINITY; case BF: return relationSelectivity; case FB: return inverseRelation == null ? Double.POSITIVE_INFINITY : inverseRelationSelectivity; case BB: return 0.1; default: throw new IllegalArgumentException(); } } @Override public int getRequiredVariablesMask() { return inverseRelation == null ? BF : FF; } private static final Name GET_OBJECTS = Name.create("Simantics/DB", "#"); private static final Name HAS_STATEMENT = Name.create("Simantics/DB", "existsStatement3"); @Override public void generate(long location, QueryCompilationContext context, Type[] typeParameters, Variable[] parameters, int boundVariables) { switch(boundVariables) { case BF: context.iterateList(parameters[1], new EApply( Locations.NO_LOCATION, Types.READ_GRAPH, context.getCompilationContext().getConstant(GET_OBJECTS), new EVariable(parameters[0]), new EExternalConstant(relation, Types.RESOURCE) )); break; case FB: if(inverseRelation == null) throw new IllegalArgumentException(); context.iterateList(parameters[0], new EApply( Locations.NO_LOCATION, Types.READ_GRAPH, context.getCompilationContext().getConstant(GET_OBJECTS), new EVariable(parameters[1]), new EExternalConstant(inverseRelation, Types.RESOURCE) )); break; case BB: context.condition( inverseRelation == null || relationSelectivity <= inverseRelationSelectivity ? new EApply( Locations.NO_LOCATION, Types.READ_GRAPH, context.getCompilationContext().getConstant(HAS_STATEMENT), new Expression[] { new EVariable(parameters[0]), new EExternalConstant(relation, Types.RESOURCE), new EVariable(parameters[1]) } ) : new EApply( Locations.NO_LOCATION, Types.READ_GRAPH, context.getCompilationContext().getConstant(HAS_STATEMENT), new Expression[] { new EVariable(parameters[1]), new EExternalConstant(inverseRelation, Types.RESOURCE), new EVariable(parameters[0]) } )); break; default: throw new IllegalArgumentException(); } } private static final Name CLAIM = Name.create("Simantics/DB", "claim"); @Override public Expression generateEnforce(long location, EnforcingContext context, Type[] typeParameters, Variable[] parameters) { return new EApply( Locations.NO_LOCATION, Types.WRITE_GRAPH, context.getCompilationContext().getConstant(CLAIM), new EVariable(parameters[0]), new EExternalConstant(relation, Types.RESOURCE), new EVariable(parameters[1]) ); } @Override public int getPhase() { return 0; } @Override public void generateIterate(PlanContext context, CodeWriter w, long location, int boundMask, Variable[] variables, Expression[] expressions) { Environment env = context.context.environment; switch(boundMask) { case BF: context.iterateList(location, w, variables[1], w.apply(location, env.getValue(GET_OBJECTS).getValue(), expressions[0].toVal(env, w), w.getModuleWriter().getExternalConstant(relation, Types.RESOURCE))); break; case FB: if(inverseRelation == null) throw new IllegalArgumentException(); context.iterateList(location, w, variables[0], w.apply(location, env.getValue(GET_OBJECTS).getValue(), expressions[1].toVal(env, w), w.getModuleWriter().getExternalConstant(inverseRelation, Types.RESOURCE))); break; case BB: context.check(location, w, inverseRelation == null || relationSelectivity <= inverseRelationSelectivity ? w.apply(location, env.getValue(HAS_STATEMENT).getValue(), expressions[0].toVal(env, w), w.getModuleWriter().getExternalConstant(relation, Types.RESOURCE), expressions[1].toVal(env, w)) : w.apply(location, env.getValue(HAS_STATEMENT).getValue(), expressions[1].toVal(env, w), w.getModuleWriter().getExternalConstant(inverseRelation, Types.RESOURCE), expressions[0].toVal(env, w))); break; default: throw new IllegalArgumentException(); } } @Override public void generateEnforce(PlanContext context, CodeWriter w, long location, Expression[] parameters) { Environment env = context.context.environment; w.apply(location, env.getValue(CLAIM).getValue(), parameters[0].toVal(env, w), w.getModuleWriter().getExternalConstant(relation, Types.RESOURCE), parameters[1].toVal(env, w)); } }