From 51c1a6ff28f26675b4a225d58f025c55911ff3a7 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 15 Aug 2021 21:50:19 +0200 Subject: Initial commit of query integration, adapting the work of Attila Ficsor Co-authored-by: Ficsor Attila - */ -public class RelationView{ - protected final Model model; - protected final RelationRepresentation representation; - protected final BiPredicate filter; - protected final BiFunction mapping; - - protected RelationView(Model model, RelationRepresentation representation, BiPredicate filter, BiFunction mapping) { - this.model = model; - this.representation = representation; - this.filter = filter; - this.mapping = mapping; - checkRepresentation(model, representation); - } - - protected RelationView(Model model, RelationRepresentation representation, BiPredicate filter) { - this.model = model; - this.representation = representation; - this.filter = filter; - this.mapping = ((k,v)->toTuple1Array(k)); - checkRepresentation(model, representation); - } - protected RelationView(Model model, RelationRepresentation representation, BiFunction mapping) { - this.model = model; - this.representation = representation; - this.filter = ((k,v)->true); - this.mapping = mapping; - checkRepresentation(model, representation); - } - protected RelationView(Model model, RelationRepresentation representation) { - this.model = model; - this.representation = representation; - this.filter = ((k,v)->true); - this.mapping = (RelationView::toTuple1ArrayPlusValue); - checkRepresentation(model, representation); - } - - private void checkRepresentation(Model model, RelationRepresentation representation) { - if(!model.getDataRepresentations().contains(representation)) { - throw new IllegalArgumentException("Selected model does not contain representation " + representation + "!"); - } - } - - public Model getModel() { - return model; - } - public RelationRepresentation getRepresentation() { - return representation; - } - - public boolean get(Tuple t) { - D value = model.get(representation, t); - return value != representation.getDefaultValue() && filter.test(t,value); - } - - public Iterable getAll() { - return (()->new CursorAsIterator<>(model.getAll(representation), mapping, filter)); - } - - public static Object[] toTuple1Array(Tuple t) { - Object[] result = new Object[t.getSize()]; - for(int i = 0; i Object[] toTuple1ArrayPlusValue(Tuple t, D value) { - Object[] result = new Object[t.getSize()+1]; - for(int i = 0; i> relationViews) { + updateListener = new RelationUpdateListener(relationViews); + } + + public void processUpdate(RelationView relationView, Tuple key, D oldValue, D newValue) { + updateListener.processChange(relationView, key, oldValue, newValue); + } + + @Override + protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, + Logger logger) { + return new RelationalEngineContext(this.updateListener); + } +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java new file mode 100644 index 00000000..042ec3dc --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/DummyBaseIndexer.java @@ -0,0 +1,59 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Callable; + +import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; +import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; +import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver; +import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener; + +/** + * copied from org.eclipse.viatra.query.runtime.tabular.TabularEngineContext; + */ +public class DummyBaseIndexer implements IBaseIndex{ + + @Override + public V coalesceTraversals(Callable callable) throws InvocationTargetException { + try { + return callable.call(); + } catch (Exception e) { + throw new InvocationTargetException(e); + } + } + + @Override + public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { + // no notification support + } + + @Override + public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { + // no notification support + } + + @Override + public void resampleDerivedFeatures() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addIndexingErrorListener(IIndexingErrorListener listener) { + return true; + } + + @Override + public boolean removeIndexingErrorListener(IIndexingErrorListener listener) { + return true; + } + + @Override + public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) { + return true; + } + + @Override + public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) { + return true; + } +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java new file mode 100644 index 00000000..b7d3b866 --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListener.java @@ -0,0 +1,51 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; +import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; +import org.eclipse.viatra.solver.data.model.Tuple; +import org.eclipse.viatra.solver.data.query.relationView.RelationView; + +public class RelationUpdateListener { + private final Map,Set>> view2Listeners; + + public RelationUpdateListener(Set> relationViews) { + view2Listeners = new HashMap<>(); + for(RelationView relationView : relationViews) { + view2Listeners.put(relationView, new HashSet<>()); + } + } + public boolean containsRelationalView(RelationViewKey relationalKey) { + RelationView relationView = relationalKey.getWrappedKey(); + return view2Listeners.containsKey(relationView); + } + public void addListener(RelationViewKey relationalKey, ITuple seed, IQueryRuntimeContextListener listener) { + RelationView relationView = relationalKey.getWrappedKey(); + if(view2Listeners.containsKey(relationView)) { + RelationUpdateListenerEntry entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener); + view2Listeners.get(relationView).add(entry); + } else throw new IllegalArgumentException(); + } + public void removeListener(RelationViewKey relationalKey, ITuple seed, IQueryRuntimeContextListener listener) { + RelationView relationView = relationalKey.getWrappedKey(); + if(view2Listeners.containsKey(relationView)) { + RelationUpdateListenerEntry entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener); + view2Listeners.get(relationView).remove(entry); + } else throw new IllegalArgumentException(); + } + + public void processChange(RelationView relationView, Tuple tuple, D oldValue, D newValue) { + Set> listeners = view2Listeners.get(relationView); + if(listeners != null) { + for(RelationUpdateListenerEntry listener : listeners) { + @SuppressWarnings("unchecked") + RelationUpdateListenerEntry typeCorrectListener = (RelationUpdateListenerEntry) listener; + typeCorrectListener.processChange(tuple, oldValue, newValue); + } + } else throw new IllegalArgumentException("View was not indexed in constructor "+relationView); + } +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java new file mode 100644 index 00000000..def02aa5 --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationUpdateListenerEntry.java @@ -0,0 +1,62 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import java.util.Arrays; +import java.util.Objects; + +import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; +import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; +import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; +import org.eclipse.viatra.solver.data.model.Tuple; + +public class RelationUpdateListenerEntry { + final RelationViewKey key; + final ITuple filter; + final IQueryRuntimeContextListener listener; + + public RelationUpdateListenerEntry(RelationViewKey key, ITuple filter, IQueryRuntimeContextListener listener) { + super(); + this.key = key; + this.filter = filter; + this.listener = listener; + } + + public void processChange(Tuple tuple, D oldValue, D newValue) { + Object[] oldTuple = isMatching(key.getWrappedKey().transform(tuple, oldValue), filter); + Object[] newTuple = isMatching(key.getWrappedKey().transform(tuple, newValue), filter); + + if(!Arrays.equals(oldTuple, newTuple)) { + if(oldTuple != null) { + listener.update(key, Tuples.flatTupleOf(oldTuple), false); + } + if(newTuple != null) { + listener.update(key, Tuples.flatTupleOf(newTuple), true); + } + } + } + + private Object[] isMatching(Object[] tuple, ITuple filter) { + for(int i = 0; i other = (RelationUpdateListenerEntry) obj; + return Objects.equals(filter, other.filter) && Objects.equals(key, other.key) + && Objects.equals(listener, other.listener); + } +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationViewKey.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationViewKey.java new file mode 100644 index 00000000..f73ddd25 --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationViewKey.java @@ -0,0 +1,59 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper; +import org.eclipse.viatra.solver.data.query.relationView.RelationView; + +public class RelationViewKey extends BaseInputKeyWrapper>{ + private final String uniqueName; + + + public RelationViewKey(RelationView wrappedKey) { + super(wrappedKey); + this.uniqueName = wrappedKey.getRepresentation().getName() + "-"+UUID.randomUUID(); + } + + @Override + public String getPrettyPrintableName() { + return wrappedKey.getRepresentation().getName(); + } + + @Override + public String getStringID() { + return uniqueName; + } + + @Override + public int getArity() { + return wrappedKey.getRepresentation().getSymbol().getArity(); + } + + @Override + public boolean isEnumerable() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(uniqueName); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof RelationViewKey)) + return false; + RelationViewKey other = (RelationViewKey) obj; + return Objects.equals(uniqueName, other.uniqueName); + } + + +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java new file mode 100644 index 00000000..c46da1bb --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalEngineContext.java @@ -0,0 +1,31 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; +import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; +import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; + +public class RelationalEngineContext implements IEngineContext{ + private final IBaseIndex baseIndex = new DummyBaseIndexer(); + private final RelationalRuntimeContext runtimeContext; + + + public RelationalEngineContext(RelationUpdateListener updateListener) { + runtimeContext = new RelationalRuntimeContext(updateListener); + } + + @Override + public IBaseIndex getBaseIndex() { + return this.baseIndex; + } + + @Override + public void dispose() { + //lifecycle not controlled by engine + } + + @Override + public IQueryRuntimeContext getQueryRuntimeContext() { + return runtimeContext; + } + +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java new file mode 100644 index 00000000..457ed6fd --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalQueryMetaContext.java @@ -0,0 +1,56 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; +import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; +import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; + +/** + * The meta context information for String scopes. + */ +public final class RelationalQueryMetaContext extends AbstractQueryMetaContext { + + @Override + public boolean isEnumerable(IInputKey key) { + ensureValidKey(key); + return key.isEnumerable(); + } + + @Override + public boolean isStateless(IInputKey key) { + ensureValidKey(key); + return key instanceof RelationViewKey; + } + + @Override + public Collection getImplications(IInputKey implyingKey) { + ensureValidKey(implyingKey); + return new HashSet(); + } + + @Override + public Map, Set> getFunctionalDependencies(IInputKey key) { + ensureValidKey(key); + if (key instanceof RelationViewKey) { + return new HashMap, Set>(); + } else { + return Collections.emptyMap(); + } + } + + public void ensureValidKey(IInputKey key) { + if (! (key instanceof RelationViewKey)) + illegalInputKey(key); + } + + public void illegalInputKey(IInputKey key) { + throw new IllegalArgumentException("The input key " + key + " is not a valid input key."); + } + +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java new file mode 100644 index 00000000..8f6fb8dd --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/internal/RelationalRuntimeContext.java @@ -0,0 +1,183 @@ +package org.eclipse.viatra.solver.data.query.internal; + +import static org.eclipse.viatra.solver.data.util.CollectionsUtil.filter; +import static org.eclipse.viatra.solver.data.util.CollectionsUtil.map; + +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; +import java.util.Optional; +import java.util.concurrent.Callable; + +import org.eclipse.viatra.query.runtime.base.core.NavigationHelperImpl; +import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; +import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; +import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; +import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; +import org.eclipse.viatra.query.runtime.matchers.context.IndexingService; +import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; +import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; +import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; +import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; +import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; + +public class RelationalRuntimeContext implements IQueryRuntimeContext { + private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext(); + private final RelationUpdateListener relationUpdateListener; + + public RelationalRuntimeContext(RelationUpdateListener relationUpdateListener) { + this.relationUpdateListener = relationUpdateListener; + } + + @Override + public IQueryMetaContext getMetaContext() { + return metaContext; + } + + // + /** + * TODO: check {@link NavigationHelperImpl#coalesceTraversals(Callable)} + */ + @Override + public V coalesceTraversals(Callable callable) throws InvocationTargetException { + try { + return callable.call(); + } catch (Exception e) { + throw new InvocationTargetException(e); + } + } + + @Override + public boolean isCoalescing() { + return true; + } + + @Override + public boolean isIndexed(IInputKey key, IndexingService service) { + if(key instanceof RelationViewKey) { + RelationViewKey relationalKey = (RelationViewKey) key; + return this.relationUpdateListener.containsRelationalView(relationalKey); + } else { + return false; + } + } + + @Override + public void ensureIndexed(IInputKey key, IndexingService service) { + if(!isIndexed(key, service)) { + throw new IllegalStateException("Engine tries to index a new key " +key); + } + } + + RelationViewKey checkKey(IInputKey key) { + if(key instanceof RelationViewKey) { + RelationViewKey relationViewKey = (RelationViewKey) key; + if(relationUpdateListener.containsRelationalView(relationViewKey)) { + return relationViewKey; + } else { + throw new IllegalStateException("Query is asking for non-indexed key"); + } + } else { + throw new IllegalStateException("Query is asking for non-relational key"); + } + } + + @Override + public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { + RelationViewKey relationalViewKey = checkKey(key); + Iterable allObjects = relationalViewKey.getWrappedKey().getAll(); + Iterable filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed)); + Iterator iterator = filteredBySeed.iterator(); + int result = 0; + while(iterator.hasNext()) { + iterator.next(); + result++; + } + return result; + } + + @Override + public Optional estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { + return Optional.empty(); + } + + @Override + public Iterable enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { + RelationViewKey relationalViewKey = checkKey(key); + Iterable allObjects = relationalViewKey.getWrappedKey().getAll(); + Iterable filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed)); + return map(filteredBySeed,Tuples::flatTupleOf); + } + + private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) { + for(int i=0; i relationalViewKey, TupleMask seedMask, ITuple seed) { +// final int arity = relationalViewKey.getArity(); +// Object[] result = new Object[arity]; +// for(int i = 0; i enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) { + return enumerateTuples(key, seedMask, seed); + } + + @Override + public boolean containsTuple(IInputKey key, ITuple seed) { + RelationViewKey relationalViewKey = checkKey(key); + return relationalViewKey.getWrappedKey().get(seed.getElements()); + } + + @Override + public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { + RelationViewKey relationalKey = checkKey(key); + this.relationUpdateListener.addListener(relationalKey, seed, listener); + + } + + @Override + public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { + RelationViewKey relationalKey = checkKey(key); + this.relationUpdateListener.removeListener(relationalKey, seed, listener); + } + + @Override + public Object wrapElement(Object externalElement) { + return externalElement; + } + + @Override + public Object unwrapElement(Object internalElement) { + return internalElement; + } + + @Override + public Tuple wrapTuple(Tuple externalElements) { + return externalElements; + } + + @Override + public Tuple unwrapTuple(Tuple internalElements) { + return internalElements; + } + + @Override + public void ensureWildcardIndexing(IndexingService service) { + throw new UnsupportedOperationException(); + } + + @Override + public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException { + runnable.run(); + } +} diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FilteredRelationView.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FilteredRelationView.java new file mode 100644 index 00000000..b5660041 --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FilteredRelationView.java @@ -0,0 +1,35 @@ +package org.eclipse.viatra.solver.data.query.relationView; + +import org.eclipse.viatra.solver.data.model.Model; +import org.eclipse.viatra.solver.data.model.Tuple; +import org.eclipse.viatra.solver.data.model.Tuple.Tuple1; +import org.eclipse.viatra.solver.data.model.representation.RelationRepresentation; + +public abstract class FilteredRelationView extends RelationView{ + + protected FilteredRelationView(Model model, RelationRepresentation representation) { + super(model, representation); + } + @Override + protected Object[] forwardMap(Tuple key, D value) { + return toTuple1Array(key); + } + @Override + public boolean get(Object[] tuple) { + int[] content = new int[tuple.length]; + for(int i = 0; i extends RelationView { + + protected FunctionalRelationView(Model model, RelationRepresentation representation) { + super(model, representation); + } + + @Override + protected boolean filter(Tuple key, D value) { + return true; + } + + @Override + protected Object[] forwardMap(Tuple key, D value) { + return toTuple1ArrayPlusValue(key, value); + } + + @Override + public boolean get(Object[] tuple) { + int[] content = new int[tuple.length-1]; + for(int i = 0; i Object[] toTuple1ArrayPlusValue(Tuple t, D value) { + Object[] result = new Object[t.getSize()+1]; + for(int i = 0; i + */ +public abstract class RelationView{ + protected final Model model; + protected final RelationRepresentation representation; + + protected RelationView(Model model, RelationRepresentation representation) { + this.model = model; + this.representation = representation; + } + + public Model getModel() { + return model; + } + public RelationRepresentation getRepresentation() { + return representation; + } + + protected abstract boolean filter(Tuple key, D value); + protected abstract Object[] forwardMap(Tuple key, D value); + public abstract boolean get(Object[] tuple); + + public Object[] transform(Tuple tuple, D value) { + if(filter(tuple, value)) { + return forwardMap(tuple, value); + } else return null; + } + + public Iterable getAll() { + return (()->new CursorAsIterator<>(model.getAll(representation), (k,v)->forwardMap(k,v), (k,v)->filter(k,v))); + } +} -- cgit v1.2.3-70-g09d2