--- /dev/null
+package org.simantics.scl.compiler.elaboration.query;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.map.hash.TIntObjectHashMap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+import org.simantics.scl.compiler.elaboration.contexts.ReplaceContext;
+import org.simantics.scl.compiler.elaboration.expressions.EConstant;
+import org.simantics.scl.compiler.elaboration.expressions.ESimpleLet;
+import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.QueryTransformer;
+import org.simantics.scl.compiler.elaboration.expressions.Variable;
+import org.simantics.scl.compiler.elaboration.java.Builtins;
+import org.simantics.scl.compiler.elaboration.query.compilation.ConstraintCollectionContext;
+import org.simantics.scl.compiler.elaboration.query.compilation.DerivateException;
+import org.simantics.scl.compiler.elaboration.query.compilation.EnforcingContext;
+import org.simantics.scl.compiler.elaboration.query.compilation.UnsolvableQueryException;
+import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
+import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
+import org.simantics.scl.compiler.types.Types;
+
+
+public class QConjunction extends QAbstractCombiner {
+
+ public QConjunction(Query ... queries) {
+ super(queries);
+ }
+
+ public QConjunction(Collection<Query> queries) {
+ this(queries.toArray(new Query[queries.size()]));
+ }
+
+ public Expression generateEnforce(EnforcingContext context) {
+ Expression result = new EConstant(Builtins.TUPLE_CONSTRUCTORS[0]);
+ for(int i=queries.length-1;i>=0;--i)
+ result = new ESimpleLet(
+ new Variable("_", Types.tupleConstructor(0)),
+ queries[i].generateEnforce(context),
+ result
+ );
+ return result;
+ }
+
+ @Override
+ public void collectConstraints(ConstraintCollectionContext context) throws UnsolvableQueryException {
+ for(Query query : queries)
+ query.collectConstraints(context);
+ }
+
+ private static class DerEntry {
+ Query base;
+ Diff[] diffs;
+
+ public DerEntry(Query base, Diff[] diffs) {
+ this.base = base;
+ this.diffs = diffs;
+ }
+ }
+
+ @Override
+ public Diff[] derivate(THashMap<LocalRelation, Diffable> diffables) throws DerivateException {
+ ArrayList<Query> cons = new ArrayList<Query>(queries.length);
+ ArrayList<DerEntry> ders = new ArrayList<DerEntry>(queries.length);
+ for(Query query : queries) {
+ Diff[] diffs = query.derivate(diffables);
+ if(diffs.length == 0)
+ cons.add(query);
+ else
+ ders.add(new DerEntry(query, diffs));
+ }
+ if(ders.isEmpty())
+ return NO_DIFF;
+
+ Query base = new QConjunction(cons.toArray(new Query[cons.size()]));
+ Diff[] diffs = NO_DIFF;
+ for(DerEntry entry : ders) {
+ ArrayList<Diff> newDiffs = new ArrayList<Diff>();
+ for(Diff diff : diffs)
+ newDiffs.add(new Diff(diff.id, new QConjunction(diff.query, entry.base)));
+ for(Diff newDiff : entry.diffs) {
+ newDiffs.add(new Diff(newDiff.id, new QConjunction(base, newDiff.query)));
+ for(Diff diff : diffs)
+ if(diff.id == newDiff.id)
+ newDiffs.add(new Diff(diff.id, new QConjunction(diff.query, newDiff.query)));
+ }
+ base = new QConjunction(base, entry.base);
+ diffs = newDiffs.toArray(new Diff[newDiffs.size()]);
+ }
+ return diffs;
+ }
+
+ @Override
+ public Query removeRelations(Set<SCLRelation> relations) {
+ for(int i=0;i<queries.length;++i) {
+ Query query = queries[i];
+ Query newQuery = query.removeRelations(relations);
+ if(query != newQuery) {
+ if(newQuery == EMPTY_QUERY)
+ return EMPTY_QUERY;
+ Query[] newQueries = new Query[queries.length];
+ if(i > 0)
+ System.arraycopy(queries, 0, newQueries, 0, i-1);
+ newQueries[i] = newQuery;
+ for(++i;i<queries.length;++i) {
+ query = queries[i];
+ newQuery = query.removeRelations(relations);
+ if(newQuery == EMPTY_QUERY)
+ return EMPTY_QUERY;
+ newQueries[i] = query;
+ }
+ return new QConjunction(newQueries);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public Query replace(ReplaceContext context) {
+ Query[] newQueries = new Query[queries.length];
+ for(int i=0;i<queries.length;++i)
+ newQueries[i] = queries[i].replace(context);
+ return new QConjunction(newQueries);
+ }
+
+ @Override
+ public void accept(QueryVisitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public void splitToPhases(TIntObjectHashMap<ArrayList<Query>> result) {
+ for(Query query : queries)
+ query.splitToPhases(result);
+ }
+
+ @Override
+ public Query accept(QueryTransformer transformer) {
+ return transformer.transform(this);
+ }
+
+}