diff options
author | 2021-08-15 21:50:19 +0200 | |
---|---|---|
committer | 2021-08-15 21:50:19 +0200 | |
commit | 51c1a6ff28f26675b4a225d58f025c55911ff3a7 (patch) | |
tree | 09df2bca3f1c344acc85431676911bebb9b3f470 /model-data | |
parent | :lipstick: (diff) | |
download | refinery-51c1a6ff28f26675b4a225d58f025c55911ff3a7.tar.gz refinery-51c1a6ff28f26675b4a225d58f025c55911ff3a7.tar.zst refinery-51c1a6ff28f26675b4a225d58f025c55911ff3a7.zip |
Initial commit of query integration, adapting the work of Attila Ficsor
Co-authored-by: Ficsor Attila <ficsorattila96@gmail.com
Diffstat (limited to 'model-data')
12 files changed, 655 insertions, 90 deletions
diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationView.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationView.java deleted file mode 100644 index c52b9ff0..00000000 --- a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationView.java +++ /dev/null | |||
@@ -1,90 +0,0 @@ | |||
1 | package org.eclipse.viatra.solver.data.query; | ||
2 | |||
3 | import java.util.function.BiFunction; | ||
4 | import java.util.function.BiPredicate; | ||
5 | |||
6 | import org.eclipse.viatra.solver.data.map.CursorAsIterator; | ||
7 | import org.eclipse.viatra.solver.data.model.Model; | ||
8 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
9 | import org.eclipse.viatra.solver.data.model.representation.RelationRepresentation; | ||
10 | |||
11 | /** | ||
12 | * Represents a view of a {@link RelationRepresentation} that can be queried. | ||
13 | * @author Oszkar Semerath | ||
14 | * | ||
15 | * @param <D> | ||
16 | */ | ||
17 | public class RelationView<D>{ | ||
18 | protected final Model model; | ||
19 | protected final RelationRepresentation<D> representation; | ||
20 | protected final BiPredicate<Tuple, D> filter; | ||
21 | protected final BiFunction<Tuple,D,Object[]> mapping; | ||
22 | |||
23 | protected RelationView(Model model, RelationRepresentation<D> representation, BiPredicate<Tuple,D> filter, BiFunction<Tuple,D,Object[]> mapping) { | ||
24 | this.model = model; | ||
25 | this.representation = representation; | ||
26 | this.filter = filter; | ||
27 | this.mapping = mapping; | ||
28 | checkRepresentation(model, representation); | ||
29 | } | ||
30 | |||
31 | protected RelationView(Model model, RelationRepresentation<D> representation, BiPredicate<Tuple,D> filter) { | ||
32 | this.model = model; | ||
33 | this.representation = representation; | ||
34 | this.filter = filter; | ||
35 | this.mapping = ((k,v)->toTuple1Array(k)); | ||
36 | checkRepresentation(model, representation); | ||
37 | } | ||
38 | protected RelationView(Model model, RelationRepresentation<D> representation, BiFunction<Tuple,D,Object[]> mapping) { | ||
39 | this.model = model; | ||
40 | this.representation = representation; | ||
41 | this.filter = ((k,v)->true); | ||
42 | this.mapping = mapping; | ||
43 | checkRepresentation(model, representation); | ||
44 | } | ||
45 | protected RelationView(Model model, RelationRepresentation<D> representation) { | ||
46 | this.model = model; | ||
47 | this.representation = representation; | ||
48 | this.filter = ((k,v)->true); | ||
49 | this.mapping = (RelationView::toTuple1ArrayPlusValue); | ||
50 | checkRepresentation(model, representation); | ||
51 | } | ||
52 | |||
53 | private void checkRepresentation(Model model, RelationRepresentation<D> representation) { | ||
54 | if(!model.getDataRepresentations().contains(representation)) { | ||
55 | throw new IllegalArgumentException("Selected model does not contain representation " + representation + "!"); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | public Model getModel() { | ||
60 | return model; | ||
61 | } | ||
62 | public RelationRepresentation<D> getRepresentation() { | ||
63 | return representation; | ||
64 | } | ||
65 | |||
66 | public boolean get(Tuple t) { | ||
67 | D value = model.get(representation, t); | ||
68 | return value != representation.getDefaultValue() && filter.test(t,value); | ||
69 | } | ||
70 | |||
71 | public Iterable<Object[]> getAll() { | ||
72 | return (()->new CursorAsIterator<>(model.getAll(representation), mapping, filter)); | ||
73 | } | ||
74 | |||
75 | public static Object[] toTuple1Array(Tuple t) { | ||
76 | Object[] result = new Object[t.getSize()]; | ||
77 | for(int i = 0; i<t.getSize(); i++) { | ||
78 | result[i] = t.get(i); | ||
79 | } | ||
80 | return result; | ||
81 | } | ||
82 | public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) { | ||
83 | Object[] result = new Object[t.getSize()+1]; | ||
84 | for(int i = 0; i<t.getSize(); i++) { | ||
85 | result[i] = t.get(i); | ||
86 | } | ||
87 | result[t.getSize()] = value; | ||
88 | return result; | ||
89 | } | ||
90 | } | ||
diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java new file mode 100644 index 00000000..66d55885 --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/RelationalScope.java | |||
@@ -0,0 +1,31 @@ | |||
1 | package org.eclipse.viatra.solver.data.query; | ||
2 | |||
3 | import java.util.Set; | ||
4 | |||
5 | import org.apache.log4j.Logger; | ||
6 | import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; | ||
7 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | ||
8 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; | ||
9 | import org.eclipse.viatra.query.runtime.api.scope.QueryScope; | ||
10 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
11 | import org.eclipse.viatra.solver.data.query.internal.RelationUpdateListener; | ||
12 | import org.eclipse.viatra.solver.data.query.internal.RelationalEngineContext; | ||
13 | import org.eclipse.viatra.solver.data.query.relationView.RelationView; | ||
14 | |||
15 | public class RelationalScope extends QueryScope{ | ||
16 | private final RelationUpdateListener updateListener; | ||
17 | |||
18 | public RelationalScope(Set<RelationView<?>> relationViews) { | ||
19 | updateListener = new RelationUpdateListener(relationViews); | ||
20 | } | ||
21 | |||
22 | public <D> void processUpdate(RelationView<D> relationView, Tuple key, D oldValue, D newValue) { | ||
23 | updateListener.processChange(relationView, key, oldValue, newValue); | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, | ||
28 | Logger logger) { | ||
29 | return new RelationalEngineContext(this.updateListener); | ||
30 | } | ||
31 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import java.lang.reflect.InvocationTargetException; | ||
4 | import java.util.concurrent.Callable; | ||
5 | |||
6 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; | ||
7 | import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener; | ||
8 | import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver; | ||
9 | import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener; | ||
10 | |||
11 | /** | ||
12 | * copied from org.eclipse.viatra.query.runtime.tabular.TabularEngineContext; | ||
13 | */ | ||
14 | public class DummyBaseIndexer implements IBaseIndex{ | ||
15 | |||
16 | @Override | ||
17 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
18 | try { | ||
19 | return callable.call(); | ||
20 | } catch (Exception e) { | ||
21 | throw new InvocationTargetException(e); | ||
22 | } | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { | ||
27 | // no notification support | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { | ||
32 | // no notification support | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public void resampleDerivedFeatures() { | ||
37 | throw new UnsupportedOperationException(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public boolean addIndexingErrorListener(IIndexingErrorListener listener) { | ||
42 | return true; | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public boolean removeIndexingErrorListener(IIndexingErrorListener listener) { | ||
47 | return true; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) { | ||
52 | return true; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) { | ||
57 | return true; | ||
58 | } | ||
59 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.HashSet; | ||
5 | import java.util.Map; | ||
6 | import java.util.Set; | ||
7 | |||
8 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
10 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
11 | import org.eclipse.viatra.solver.data.query.relationView.RelationView; | ||
12 | |||
13 | public class RelationUpdateListener { | ||
14 | private final Map<RelationView<?>,Set<RelationUpdateListenerEntry<?>>> view2Listeners; | ||
15 | |||
16 | public RelationUpdateListener(Set<RelationView<?>> relationViews) { | ||
17 | view2Listeners = new HashMap<>(); | ||
18 | for(RelationView<?> relationView : relationViews) { | ||
19 | view2Listeners.put(relationView, new HashSet<>()); | ||
20 | } | ||
21 | } | ||
22 | public boolean containsRelationalView(RelationViewKey<?> relationalKey) { | ||
23 | RelationView<?> relationView = relationalKey.getWrappedKey(); | ||
24 | return view2Listeners.containsKey(relationView); | ||
25 | } | ||
26 | public void addListener(RelationViewKey<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) { | ||
27 | RelationView<?> relationView = relationalKey.getWrappedKey(); | ||
28 | if(view2Listeners.containsKey(relationView)) { | ||
29 | RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener); | ||
30 | view2Listeners.get(relationView).add(entry); | ||
31 | } else throw new IllegalArgumentException(); | ||
32 | } | ||
33 | public void removeListener(RelationViewKey<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) { | ||
34 | RelationView<?> relationView = relationalKey.getWrappedKey(); | ||
35 | if(view2Listeners.containsKey(relationView)) { | ||
36 | RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener); | ||
37 | view2Listeners.get(relationView).remove(entry); | ||
38 | } else throw new IllegalArgumentException(); | ||
39 | } | ||
40 | |||
41 | public <D> void processChange(RelationView<D> relationView, Tuple tuple, D oldValue, D newValue) { | ||
42 | Set<RelationUpdateListenerEntry<?>> listeners = view2Listeners.get(relationView); | ||
43 | if(listeners != null) { | ||
44 | for(RelationUpdateListenerEntry<?> listener : listeners) { | ||
45 | @SuppressWarnings("unchecked") | ||
46 | RelationUpdateListenerEntry<D> typeCorrectListener = (RelationUpdateListenerEntry<D>) listener; | ||
47 | typeCorrectListener.processChange(tuple, oldValue, newValue); | ||
48 | } | ||
49 | } else throw new IllegalArgumentException("View was not indexed in constructor "+relationView); | ||
50 | } | ||
51 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Objects; | ||
5 | |||
6 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
8 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
9 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
10 | |||
11 | public class RelationUpdateListenerEntry<D> { | ||
12 | final RelationViewKey<D> key; | ||
13 | final ITuple filter; | ||
14 | final IQueryRuntimeContextListener listener; | ||
15 | |||
16 | public RelationUpdateListenerEntry(RelationViewKey<D> key, ITuple filter, IQueryRuntimeContextListener listener) { | ||
17 | super(); | ||
18 | this.key = key; | ||
19 | this.filter = filter; | ||
20 | this.listener = listener; | ||
21 | } | ||
22 | |||
23 | public void processChange(Tuple tuple, D oldValue, D newValue) { | ||
24 | Object[] oldTuple = isMatching(key.getWrappedKey().transform(tuple, oldValue), filter); | ||
25 | Object[] newTuple = isMatching(key.getWrappedKey().transform(tuple, newValue), filter); | ||
26 | |||
27 | if(!Arrays.equals(oldTuple, newTuple)) { | ||
28 | if(oldTuple != null) { | ||
29 | listener.update(key, Tuples.flatTupleOf(oldTuple), false); | ||
30 | } | ||
31 | if(newTuple != null) { | ||
32 | listener.update(key, Tuples.flatTupleOf(newTuple), true); | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | |||
37 | private Object[] isMatching(Object[] tuple, ITuple filter) { | ||
38 | for(int i = 0; i<filter.getSize(); i++) { | ||
39 | final Object filterObject = filter.get(i); | ||
40 | if(filterObject != null && !filterObject.equals(tuple[i])) { | ||
41 | return null; | ||
42 | } | ||
43 | } | ||
44 | return tuple; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public int hashCode() { | ||
49 | return Objects.hash(filter, key, listener); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean equals(Object obj) { | ||
54 | if (this == obj) | ||
55 | return true; | ||
56 | if (!(obj instanceof RelationUpdateListenerEntry)) | ||
57 | return false; | ||
58 | RelationUpdateListenerEntry<?> other = (RelationUpdateListenerEntry<?>) obj; | ||
59 | return Objects.equals(filter, other.filter) && Objects.equals(key, other.key) | ||
60 | && Objects.equals(listener, other.listener); | ||
61 | } | ||
62 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import java.util.Objects; | ||
4 | import java.util.UUID; | ||
5 | |||
6 | import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper; | ||
7 | import org.eclipse.viatra.solver.data.query.relationView.RelationView; | ||
8 | |||
9 | public class RelationViewKey<D> extends BaseInputKeyWrapper<RelationView<D>>{ | ||
10 | private final String uniqueName; | ||
11 | |||
12 | |||
13 | public RelationViewKey(RelationView<D> wrappedKey) { | ||
14 | super(wrappedKey); | ||
15 | this.uniqueName = wrappedKey.getRepresentation().getName() + "-"+UUID.randomUUID(); | ||
16 | } | ||
17 | |||
18 | @Override | ||
19 | public String getPrettyPrintableName() { | ||
20 | return wrappedKey.getRepresentation().getName(); | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public String getStringID() { | ||
25 | return uniqueName; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getArity() { | ||
30 | return wrappedKey.getRepresentation().getSymbol().getArity(); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public boolean isEnumerable() { | ||
35 | return true; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int hashCode() { | ||
40 | final int prime = 31; | ||
41 | int result = super.hashCode(); | ||
42 | result = prime * result + Objects.hash(uniqueName); | ||
43 | return result; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public boolean equals(Object obj) { | ||
48 | if (this == obj) | ||
49 | return true; | ||
50 | if (!super.equals(obj)) | ||
51 | return false; | ||
52 | if (!(obj instanceof RelationViewKey)) | ||
53 | return false; | ||
54 | RelationViewKey<?> other = (RelationViewKey<?>) obj; | ||
55 | return Objects.equals(uniqueName, other.uniqueName); | ||
56 | } | ||
57 | |||
58 | |||
59 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; | ||
4 | import org.eclipse.viatra.query.runtime.api.scope.IEngineContext; | ||
5 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; | ||
6 | |||
7 | public class RelationalEngineContext implements IEngineContext{ | ||
8 | private final IBaseIndex baseIndex = new DummyBaseIndexer(); | ||
9 | private final RelationalRuntimeContext runtimeContext; | ||
10 | |||
11 | |||
12 | public RelationalEngineContext(RelationUpdateListener updateListener) { | ||
13 | runtimeContext = new RelationalRuntimeContext(updateListener); | ||
14 | } | ||
15 | |||
16 | @Override | ||
17 | public IBaseIndex getBaseIndex() { | ||
18 | return this.baseIndex; | ||
19 | } | ||
20 | |||
21 | @Override | ||
22 | public void dispose() { | ||
23 | //lifecycle not controlled by engine | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public IQueryRuntimeContext getQueryRuntimeContext() { | ||
28 | return runtimeContext; | ||
29 | } | ||
30 | |||
31 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import java.util.Collection; | ||
4 | import java.util.Collections; | ||
5 | import java.util.HashMap; | ||
6 | import java.util.HashSet; | ||
7 | import java.util.Map; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; | ||
11 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; | ||
13 | |||
14 | /** | ||
15 | * The meta context information for String scopes. | ||
16 | */ | ||
17 | public final class RelationalQueryMetaContext extends AbstractQueryMetaContext { | ||
18 | |||
19 | @Override | ||
20 | public boolean isEnumerable(IInputKey key) { | ||
21 | ensureValidKey(key); | ||
22 | return key.isEnumerable(); | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public boolean isStateless(IInputKey key) { | ||
27 | ensureValidKey(key); | ||
28 | return key instanceof RelationViewKey<?>; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { | ||
33 | ensureValidKey(implyingKey); | ||
34 | return new HashSet<InputKeyImplication>(); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { | ||
39 | ensureValidKey(key); | ||
40 | if (key instanceof RelationViewKey) { | ||
41 | return new HashMap<Set<Integer>, Set<Integer>>(); | ||
42 | } else { | ||
43 | return Collections.emptyMap(); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | public void ensureValidKey(IInputKey key) { | ||
48 | if (! (key instanceof RelationViewKey<?>)) | ||
49 | illegalInputKey(key); | ||
50 | } | ||
51 | |||
52 | public void illegalInputKey(IInputKey key) { | ||
53 | throw new IllegalArgumentException("The input key " + key + " is not a valid input key."); | ||
54 | } | ||
55 | |||
56 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.internal; | ||
2 | |||
3 | import static org.eclipse.viatra.solver.data.util.CollectionsUtil.filter; | ||
4 | import static org.eclipse.viatra.solver.data.util.CollectionsUtil.map; | ||
5 | |||
6 | import java.lang.reflect.InvocationTargetException; | ||
7 | import java.util.Iterator; | ||
8 | import java.util.Optional; | ||
9 | import java.util.concurrent.Callable; | ||
10 | |||
11 | import org.eclipse.viatra.query.runtime.base.core.NavigationHelperImpl; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext; | ||
14 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext; | ||
15 | import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; | ||
16 | import org.eclipse.viatra.query.runtime.matchers.context.IndexingService; | ||
17 | import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; | ||
18 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
19 | import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; | ||
20 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
21 | import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; | ||
22 | |||
23 | public class RelationalRuntimeContext implements IQueryRuntimeContext { | ||
24 | private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext(); | ||
25 | private final RelationUpdateListener relationUpdateListener; | ||
26 | |||
27 | public RelationalRuntimeContext(RelationUpdateListener relationUpdateListener) { | ||
28 | this.relationUpdateListener = relationUpdateListener; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public IQueryMetaContext getMetaContext() { | ||
33 | return metaContext; | ||
34 | } | ||
35 | |||
36 | // | ||
37 | /** | ||
38 | * TODO: check {@link NavigationHelperImpl#coalesceTraversals(Callable)} | ||
39 | */ | ||
40 | @Override | ||
41 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
42 | try { | ||
43 | return callable.call(); | ||
44 | } catch (Exception e) { | ||
45 | throw new InvocationTargetException(e); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public boolean isCoalescing() { | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public boolean isIndexed(IInputKey key, IndexingService service) { | ||
56 | if(key instanceof RelationViewKey<?>) { | ||
57 | RelationViewKey<?> relationalKey = (RelationViewKey<?>) key; | ||
58 | return this.relationUpdateListener.containsRelationalView(relationalKey); | ||
59 | } else { | ||
60 | return false; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public void ensureIndexed(IInputKey key, IndexingService service) { | ||
66 | if(!isIndexed(key, service)) { | ||
67 | throw new IllegalStateException("Engine tries to index a new key " +key); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | RelationViewKey<?> checkKey(IInputKey key) { | ||
72 | if(key instanceof RelationViewKey) { | ||
73 | RelationViewKey<?> relationViewKey = (RelationViewKey<?>) key; | ||
74 | if(relationUpdateListener.containsRelationalView(relationViewKey)) { | ||
75 | return relationViewKey; | ||
76 | } else { | ||
77 | throw new IllegalStateException("Query is asking for non-indexed key"); | ||
78 | } | ||
79 | } else { | ||
80 | throw new IllegalStateException("Query is asking for non-relational key"); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
86 | RelationViewKey<?> relationalViewKey = checkKey(key); | ||
87 | Iterable<Object[]> allObjects = relationalViewKey.getWrappedKey().getAll(); | ||
88 | Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed)); | ||
89 | Iterator<Object[]> iterator = filteredBySeed.iterator(); | ||
90 | int result = 0; | ||
91 | while(iterator.hasNext()) { | ||
92 | iterator.next(); | ||
93 | result++; | ||
94 | } | ||
95 | return result; | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
100 | return Optional.empty(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
105 | RelationViewKey<?> relationalViewKey = checkKey(key); | ||
106 | Iterable<Object[]> allObjects = relationalViewKey.getWrappedKey().getAll(); | ||
107 | Iterable<Object[]> filteredBySeed = filter(allObjects,objectArray -> isMatching(objectArray,seedMask,seed)); | ||
108 | return map(filteredBySeed,Tuples::flatTupleOf); | ||
109 | } | ||
110 | |||
111 | private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) { | ||
112 | for(int i=0; i<seedMask.indices.length; i++) { | ||
113 | final Object seedElement = seed.get(i); | ||
114 | final Object tupleElement = tuple[seedMask.indices[i]]; | ||
115 | if(!tupleElement.equals(seedElement)) { | ||
116 | return false; | ||
117 | } | ||
118 | } | ||
119 | return true; | ||
120 | } | ||
121 | // private Object[] toObjectMask(RelationViewKey<?> relationalViewKey, TupleMask seedMask, ITuple seed) { | ||
122 | // final int arity = relationalViewKey.getArity(); | ||
123 | // Object[] result = new Object[arity]; | ||
124 | // for(int i = 0; i<seedMask.indices.length; i++) { | ||
125 | // result[seedMask.indices[i]] = seed.get(i); | ||
126 | // } | ||
127 | // return result; | ||
128 | // } | ||
129 | |||
130 | @Override | ||
131 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
132 | return enumerateTuples(key, seedMask, seed); | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public boolean containsTuple(IInputKey key, ITuple seed) { | ||
137 | RelationViewKey<?> relationalViewKey = checkKey(key); | ||
138 | return relationalViewKey.getWrappedKey().get(seed.getElements()); | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { | ||
143 | RelationViewKey<?> relationalKey = checkKey(key); | ||
144 | this.relationUpdateListener.addListener(relationalKey, seed, listener); | ||
145 | |||
146 | } | ||
147 | |||
148 | @Override | ||
149 | public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { | ||
150 | RelationViewKey<?> relationalKey = checkKey(key); | ||
151 | this.relationUpdateListener.removeListener(relationalKey, seed, listener); | ||
152 | } | ||
153 | |||
154 | @Override | ||
155 | public Object wrapElement(Object externalElement) { | ||
156 | return externalElement; | ||
157 | } | ||
158 | |||
159 | @Override | ||
160 | public Object unwrapElement(Object internalElement) { | ||
161 | return internalElement; | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public Tuple wrapTuple(Tuple externalElements) { | ||
166 | return externalElements; | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | public Tuple unwrapTuple(Tuple internalElements) { | ||
171 | return internalElements; | ||
172 | } | ||
173 | |||
174 | @Override | ||
175 | public void ensureWildcardIndexing(IndexingService service) { | ||
176 | throw new UnsupportedOperationException(); | ||
177 | } | ||
178 | |||
179 | @Override | ||
180 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException { | ||
181 | runnable.run(); | ||
182 | } | ||
183 | } | ||
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 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.relationView; | ||
2 | |||
3 | import org.eclipse.viatra.solver.data.model.Model; | ||
4 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
5 | import org.eclipse.viatra.solver.data.model.Tuple.Tuple1; | ||
6 | import org.eclipse.viatra.solver.data.model.representation.RelationRepresentation; | ||
7 | |||
8 | public abstract class FilteredRelationView<D> extends RelationView<D>{ | ||
9 | |||
10 | protected FilteredRelationView(Model model, RelationRepresentation<D> representation) { | ||
11 | super(model, representation); | ||
12 | } | ||
13 | @Override | ||
14 | protected Object[] forwardMap(Tuple key, D value) { | ||
15 | return toTuple1Array(key); | ||
16 | } | ||
17 | @Override | ||
18 | public boolean get(Object[] tuple) { | ||
19 | int[] content = new int[tuple.length]; | ||
20 | for(int i = 0; i<tuple.length; i++) { | ||
21 | content[i] =((Tuple1)tuple[i]).get(0); | ||
22 | } | ||
23 | Tuple key = Tuple.of(content); | ||
24 | D value = this.model.get(representation, key); | ||
25 | return filter(key, value); | ||
26 | } | ||
27 | |||
28 | public static Object[] toTuple1Array(Tuple t) { | ||
29 | Object[] result = new Object[t.getSize()]; | ||
30 | for(int i = 0; i<t.getSize(); i++) { | ||
31 | result[i] = t.get(i); | ||
32 | } | ||
33 | return result; | ||
34 | } | ||
35 | } | ||
diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FunctionalRelationView.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FunctionalRelationView.java new file mode 100644 index 00000000..2cd4b05e --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/FunctionalRelationView.java | |||
@@ -0,0 +1,45 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.relationView; | ||
2 | |||
3 | import org.eclipse.viatra.solver.data.model.Model; | ||
4 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
5 | import org.eclipse.viatra.solver.data.model.Tuple.Tuple1; | ||
6 | import org.eclipse.viatra.solver.data.model.representation.RelationRepresentation; | ||
7 | |||
8 | public class FunctionalRelationView<D> extends RelationView<D> { | ||
9 | |||
10 | protected FunctionalRelationView(Model model, RelationRepresentation<D> representation) { | ||
11 | super(model, representation); | ||
12 | } | ||
13 | |||
14 | @Override | ||
15 | protected boolean filter(Tuple key, D value) { | ||
16 | return true; | ||
17 | } | ||
18 | |||
19 | @Override | ||
20 | protected Object[] forwardMap(Tuple key, D value) { | ||
21 | return toTuple1ArrayPlusValue(key, value); | ||
22 | } | ||
23 | |||
24 | @Override | ||
25 | public boolean get(Object[] tuple) { | ||
26 | int[] content = new int[tuple.length-1]; | ||
27 | for(int i = 0; i<tuple.length-1; i++) { | ||
28 | content[i] =((Tuple1)tuple[i]).get(0); | ||
29 | } | ||
30 | Tuple key = Tuple.of(content); | ||
31 | @SuppressWarnings("unchecked") | ||
32 | D valueInTuple = (D) tuple[tuple.length-1]; | ||
33 | D valueInMap = this.model.get(representation, key); | ||
34 | return valueInTuple.equals(valueInMap); | ||
35 | } | ||
36 | |||
37 | public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) { | ||
38 | Object[] result = new Object[t.getSize()+1]; | ||
39 | for(int i = 0; i<t.getSize(); i++) { | ||
40 | result[i] = t.get(i); | ||
41 | } | ||
42 | result[t.getSize()] = value; | ||
43 | return result; | ||
44 | } | ||
45 | } | ||
diff --git a/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/RelationView.java b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/RelationView.java new file mode 100644 index 00000000..876f1dcc --- /dev/null +++ b/model-data/src/main/java/org/eclipse/viatra/solver/data/query/relationView/RelationView.java | |||
@@ -0,0 +1,43 @@ | |||
1 | package org.eclipse.viatra.solver.data.query.relationView; | ||
2 | |||
3 | import org.eclipse.viatra.solver.data.map.CursorAsIterator; | ||
4 | import org.eclipse.viatra.solver.data.model.Model; | ||
5 | import org.eclipse.viatra.solver.data.model.Tuple; | ||
6 | import org.eclipse.viatra.solver.data.model.representation.RelationRepresentation; | ||
7 | |||
8 | /** | ||
9 | * Represents a view of a {@link RelationRepresentation} that can be queried. | ||
10 | * @author Oszkar Semerath | ||
11 | * | ||
12 | * @param <D> | ||
13 | */ | ||
14 | public abstract class RelationView<D>{ | ||
15 | protected final Model model; | ||
16 | protected final RelationRepresentation<D> representation; | ||
17 | |||
18 | protected RelationView(Model model, RelationRepresentation<D> representation) { | ||
19 | this.model = model; | ||
20 | this.representation = representation; | ||
21 | } | ||
22 | |||
23 | public Model getModel() { | ||
24 | return model; | ||
25 | } | ||
26 | public RelationRepresentation<D> getRepresentation() { | ||
27 | return representation; | ||
28 | } | ||
29 | |||
30 | protected abstract boolean filter(Tuple key, D value); | ||
31 | protected abstract Object[] forwardMap(Tuple key, D value); | ||
32 | public abstract boolean get(Object[] tuple); | ||
33 | |||
34 | public Object[] transform(Tuple tuple, D value) { | ||
35 | if(filter(tuple, value)) { | ||
36 | return forwardMap(tuple, value); | ||
37 | } else return null; | ||
38 | } | ||
39 | |||
40 | public Iterable<Object[]> getAll() { | ||
41 | return (()->new CursorAsIterator<>(model.getAll(representation), (k,v)->forwardMap(k,v), (k,v)->filter(k,v))); | ||
42 | } | ||
43 | } | ||