diff options
author | 2023-08-19 03:00:23 +0200 | |
---|---|---|
committer | 2023-08-19 03:00:23 +0200 | |
commit | fc7e3dc61a2ac8de32914af331c1144e4fa843e6 (patch) | |
tree | f17f58c9a14700e76c4b54b242c0bca061199e1f /subprojects/viatra-runtime-matchers | |
parent | chore: import VIATRA source (diff) | |
download | refinery-fc7e3dc61a2ac8de32914af331c1144e4fa843e6.tar.gz refinery-fc7e3dc61a2ac8de32914af331c1144e4fa843e6.tar.zst refinery-fc7e3dc61a2ac8de32914af331c1144e4fa843e6.zip |
refactor: remove unused VIATRA code
We don't need Eclipse platform support, table-based scopes, and EMF support.
Diffstat (limited to 'subprojects/viatra-runtime-matchers')
14 files changed, 0 insertions, 1814 deletions
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java deleted file mode 100644 index 16f40358..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/IStorageBackend.java +++ /dev/null | |||
@@ -1,53 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary; | ||
14 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterUnary; | ||
15 | |||
16 | /** | ||
17 | * An abstract storage backend that instantiates tables and coordinates transactions. | ||
18 | * | ||
19 | * <p><strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
20 | * part of a work in progress. There is no guarantee that this API will | ||
21 | * work or that it will remain the same. | ||
22 | * | ||
23 | * @author Gabor Bergmann | ||
24 | * | ||
25 | * @since 2.1 | ||
26 | */ | ||
27 | public interface IStorageBackend { | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Marks the beginning of a transaction. | ||
32 | * In transaction mode, table updates may be temporarily delayed ({@link tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable} methods may return stale answers) for better performance. | ||
33 | */ | ||
34 | void startTransaction(); | ||
35 | /** | ||
36 | * Marks the end of a transaction. | ||
37 | * Any updates delayed during the transaction must now be flushed. | ||
38 | */ | ||
39 | void finishTransaction(); | ||
40 | |||
41 | /** | ||
42 | * Creates an index table for a simple value set. | ||
43 | * @param unique client promises to only insert a given tuple with multiplicity one | ||
44 | */ | ||
45 | ITableWriterUnary.Table<Object> createUnaryTable(IInputKey key, ITableContext tableContext, boolean unique); | ||
46 | /** | ||
47 | * Creates an index table for a simple source-target bidirectional mapping. | ||
48 | * @param unique client promises to only insert a given tuple with multiplicity one | ||
49 | */ | ||
50 | ITableWriterBinary.Table<Object,Object> createBinaryTable(IInputKey key, ITableContext tableContext, boolean unique); | ||
51 | |||
52 | |||
53 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java deleted file mode 100644 index fd1f7b7e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleLocalStorageBackend.java +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.scopes.tables.SimpleBinaryTable; | ||
14 | import tools.refinery.viatra.runtime.matchers.scopes.tables.SimpleUnaryTable; | ||
15 | |||
16 | /** | ||
17 | * Basic storage backend implementation based on local collections. | ||
18 | * | ||
19 | * <p><strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
20 | * part of a work in progress. There is no guarantee that this API will | ||
21 | * work or that it will remain the same. | ||
22 | * | ||
23 | * @author Gabor Bergmann | ||
24 | * @since 2.1 | ||
25 | */ | ||
26 | public class SimpleLocalStorageBackend implements IStorageBackend { | ||
27 | |||
28 | @Override | ||
29 | public void startTransaction() { | ||
30 | // NOP | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public void finishTransaction() { | ||
35 | // NOP | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public SimpleUnaryTable<Object> createUnaryTable(IInputKey key, ITableContext tableContext, boolean unique) { | ||
40 | return new SimpleUnaryTable<>(key, tableContext, unique); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public SimpleBinaryTable<Object, Object> createBinaryTable(IInputKey key, ITableContext tableContext, | ||
45 | boolean unique) { | ||
46 | return new SimpleBinaryTable<>(key, tableContext, unique); | ||
47 | } | ||
48 | |||
49 | |||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java deleted file mode 100644 index a3a827dc..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/SimpleRuntimeContext.java +++ /dev/null | |||
@@ -1,132 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
11 | |||
12 | import java.lang.reflect.InvocationTargetException; | ||
13 | import java.util.concurrent.Callable; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IndexingService; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | |||
23 | /** | ||
24 | * A simple demo implementation of the IQRC interface using tables. | ||
25 | * | ||
26 | * <p> | ||
27 | * Usage: first, instantiate {@link IIndexTable} tables with this as the 'tableContext' argument, and call | ||
28 | * {@link #registerIndexTable(IIndexTable)} manually to register them. Afterwards, they will be visible to the query | ||
29 | * backends. | ||
30 | * <p> | ||
31 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
32 | * part of a work in progress. There is no guarantee that this API will | ||
33 | * work or that it will remain the same. | ||
34 | * | ||
35 | * @author Gabor Bergmann | ||
36 | * @since 2.0 | ||
37 | */ | ||
38 | public class SimpleRuntimeContext extends TabularRuntimeContext { | ||
39 | |||
40 | private IQueryMetaContext metaContext; | ||
41 | |||
42 | public SimpleRuntimeContext(IQueryMetaContext metaContext) { | ||
43 | this.metaContext = metaContext; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public void logError(String message) { | ||
48 | System.err.println(message); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public IQueryMetaContext getMetaContext() { | ||
53 | return metaContext; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
58 | try { | ||
59 | return callable.call(); | ||
60 | } catch (Exception e) { | ||
61 | throw new InvocationTargetException(e); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public boolean isCoalescing() { | ||
67 | return false; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean isIndexed(IInputKey key, IndexingService service) { | ||
72 | return peekIndexTable(key) != null; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public void ensureIndexed(IInputKey key, IndexingService service) { | ||
77 | if (peekIndexTable(key) == null) | ||
78 | throw new IllegalArgumentException(key.getPrettyPrintableName()); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Object wrapElement(Object externalElement) { | ||
83 | return externalElement; | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public Object unwrapElement(Object internalElement) { | ||
88 | return internalElement; | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public Tuple wrapTuple(Tuple externalElements) { | ||
93 | return externalElements; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Tuple unwrapTuple(Tuple internalElements) { | ||
98 | return internalElements; | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public void ensureWildcardIndexing(IndexingService service) { | ||
103 | throw new UnsupportedOperationException(); | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException { | ||
108 | runnable.run(); | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | protected boolean isContainedInStatelessKey(IInputKey key, ITuple seed) { | ||
113 | if (key instanceof JavaTransitiveInstancesKey) { | ||
114 | Class<?> instanceClass = forceGetWrapperInstanceClass((JavaTransitiveInstancesKey) key); | ||
115 | return instanceClass != null && instanceClass.isInstance(seed.get(0)); | ||
116 | } else | ||
117 | throw new IllegalArgumentException(key.getPrettyPrintableName()); | ||
118 | } | ||
119 | |||
120 | private Class<?> forceGetWrapperInstanceClass(JavaTransitiveInstancesKey key) { | ||
121 | Class<?> instanceClass; | ||
122 | try { | ||
123 | instanceClass = key.forceGetWrapperInstanceClass(); | ||
124 | } catch (ClassNotFoundException e) { | ||
125 | logError( | ||
126 | "Could not load instance class for type constraint " + key.getWrappedKey() + ": " + e.getMessage()); | ||
127 | instanceClass = null; | ||
128 | } | ||
129 | return instanceClass; | ||
130 | } | ||
131 | |||
132 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java deleted file mode 100644 index e99e24d3..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/TabularRuntimeContext.java +++ /dev/null | |||
@@ -1,119 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes; | ||
11 | |||
12 | import java.util.Map; | ||
13 | import java.util.Optional; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.AbstractQueryRuntimeContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
18 | import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable; | ||
19 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
25 | |||
26 | /** | ||
27 | * An abstract runtime context that serves enumerable input key instances from tables. | ||
28 | * | ||
29 | * <p> | ||
30 | * Usage: first, instantiate {@link IIndexTable} tables with this as the 'tableContext' argument. Call | ||
31 | * {@link #registerIndexTable(IIndexTable)} to register them; this may happen either during a coalesced indexing, or on | ||
32 | * external initiation. Afterwards, they will be visible to the query backends. | ||
33 | * <p> | ||
34 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
35 | * part of a work in progress. There is no guarantee that this API will | ||
36 | * work or that it will remain the same. | ||
37 | * | ||
38 | * @author Gabor Bergmann | ||
39 | * @since 2.0 | ||
40 | */ | ||
41 | public abstract class TabularRuntimeContext extends AbstractQueryRuntimeContext implements ITableContext { | ||
42 | |||
43 | private Map<IInputKey, IIndexTable> instanceTables = CollectionsFactory.createMap(); | ||
44 | |||
45 | public void registerIndexTable(IIndexTable table) { | ||
46 | IInputKey inputKey = table.getInputKey(); | ||
47 | instanceTables.put(inputKey, table); | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * @return null if the table is not registered | ||
52 | */ | ||
53 | public IIndexTable peekIndexTable(IInputKey key) { | ||
54 | return instanceTables.get(key); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * If the table is not registered, {@link #handleUnregisteredTableRequest(IInputKey)} is invoked; it may handle it | ||
59 | * by raising an error or e.g. on-demand index construction | ||
60 | */ | ||
61 | public IIndexTable getIndexTable(IInputKey key) { | ||
62 | IIndexTable table = instanceTables.get(key); | ||
63 | if (table != null) | ||
64 | return table; | ||
65 | else | ||
66 | return handleUnregisteredTableRequest(key); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Override this to provide on-demand table registration | ||
71 | */ | ||
72 | protected IIndexTable handleUnregisteredTableRequest(IInputKey key) { | ||
73 | throw new IllegalArgumentException(key.getPrettyPrintableName()); | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
78 | return getIndexTable(key).countTuples(seedMask, seed); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
83 | return getIndexTable(key).estimateProjectionSize(groupMask, requiredAccuracy); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
88 | return getIndexTable(key).enumerateTuples(seedMask, seed); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
93 | return getIndexTable(key).enumerateValues(seedMask, seed); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public boolean containsTuple(IInputKey key, ITuple seed) { | ||
98 | if (key.isEnumerable()) { | ||
99 | return getIndexTable(key).containsTuple(seed); | ||
100 | } else { | ||
101 | return isContainedInStatelessKey(key, seed); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { | ||
107 | getIndexTable(key).addUpdateListener(seed, listener); | ||
108 | } | ||
109 | @Override | ||
110 | public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { | ||
111 | getIndexTable(key).removeUpdateListener(seed, listener); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Handles non-enumerable input keys that are not backed by a table | ||
116 | */ | ||
117 | protected abstract boolean isContainedInStatelessKey(IInputKey key, ITuple seed); | ||
118 | |||
119 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java deleted file mode 100644 index 7b557c33..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/AbstractIndexTable.java +++ /dev/null | |||
@@ -1,266 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
21 | |||
22 | /** | ||
23 | * <p> | ||
24 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
25 | * part of a work in progress. There is no guarantee that this API will | ||
26 | * work or that it will remain the same. | ||
27 | * | ||
28 | * @since 2.0 | ||
29 | * @author Gabor Bergmann | ||
30 | */ | ||
31 | public abstract class AbstractIndexTable implements IIndexTable { | ||
32 | |||
33 | private IInputKey inputKey; | ||
34 | protected ITableContext tableContext; | ||
35 | |||
36 | protected final TupleMask emptyMask; | ||
37 | protected final Tuple emptyTuple; | ||
38 | |||
39 | |||
40 | public AbstractIndexTable(IInputKey inputKey, ITableContext tableContext) { | ||
41 | this.inputKey = inputKey; | ||
42 | this.tableContext = tableContext; | ||
43 | |||
44 | this.emptyMask = TupleMask.empty(getInputKey().getArity()); | ||
45 | this.emptyTuple = Tuples.flatTupleOf(new Object[inputKey.getArity()]); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public String toString() { | ||
50 | return this.getClass().getSimpleName() + ":" + inputKey.getPrettyPrintableName(); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public IInputKey getInputKey() { | ||
55 | return inputKey; | ||
56 | } | ||
57 | |||
58 | protected void logError(String message) { | ||
59 | tableContext.logError(message); | ||
60 | } | ||
61 | |||
62 | |||
63 | /// UPDATE HANDLING SECTION | ||
64 | |||
65 | // The entire mechanism is designed to accommodate a large number of update listeners, | ||
66 | // but maybe there will typically be only a single, universal (unseeded) listener? | ||
67 | // TODO Create special handling for that case. | ||
68 | |||
69 | // Invariant: true iff #listenerGroupsAndSeed is nonempty | ||
70 | protected boolean emitNotifications = false; | ||
71 | // Subscribed listeners grouped by their seed mask (e.g. all those that seed columns 2 and 5 are together), | ||
72 | // groups are stored in a list for quick delivery-time iteration (at the expense of adding / removing); | ||
73 | // individual listeners can be looked up based on their seed tuple | ||
74 | protected List<IListenersWithSameMask> listenerGroups = CollectionsFactory.createObserverList(); | ||
75 | |||
76 | |||
77 | /** | ||
78 | * Implementors shall call this to deliver all notifications. | ||
79 | * Call may be conditioned to {@link #emitNotifications} | ||
80 | */ | ||
81 | protected void deliverChangeNotifications(Tuple updateTuple, boolean isInsertion) { | ||
82 | for (IListenersWithSameMask listenersForSeed : listenerGroups) { | ||
83 | listenersForSeed.deliver(updateTuple, isInsertion); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) { | ||
89 | TupleMask seedMask; | ||
90 | if (seed == null) { | ||
91 | seed = emptyTuple; | ||
92 | seedMask = emptyMask; | ||
93 | } else { | ||
94 | seedMask = TupleMask.fromNonNullIndices(seed); | ||
95 | } | ||
96 | IListenersWithSameMask listenerGroup = getListenerGroup(seedMask); | ||
97 | if (listenerGroup == null) { // create new group | ||
98 | switch (seedMask.getSize()) { | ||
99 | case 0: | ||
100 | listenerGroup = new UniversalListeners(); | ||
101 | break; | ||
102 | case 1: | ||
103 | listenerGroup = new ColumnBoundListeners(seedMask.indices[0]); | ||
104 | break; | ||
105 | default: | ||
106 | listenerGroup = new GenericBoundListeners(seedMask); | ||
107 | } | ||
108 | listenerGroups.add(listenerGroup); | ||
109 | emitNotifications = true; | ||
110 | } | ||
111 | listenerGroup.addUpdateListener(seed, listener); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) { | ||
116 | TupleMask seedMask; | ||
117 | if (seed == null) { | ||
118 | seed = emptyTuple; | ||
119 | seedMask = emptyMask; | ||
120 | } else { | ||
121 | seedMask = TupleMask.fromNonNullIndices(seed); | ||
122 | } | ||
123 | IListenersWithSameMask listenerGroup = getListenerGroup(seedMask); | ||
124 | if (listenerGroup == null) | ||
125 | throw new IllegalStateException("no listener subscribed with mask" + seedMask); | ||
126 | |||
127 | if (listenerGroup.removeUpdateListener(seed, listener)) { | ||
128 | listenerGroups.remove(listenerGroup); | ||
129 | emitNotifications = !listenerGroups.isEmpty(); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | protected IListenersWithSameMask getListenerGroup(TupleMask seedMask) { | ||
134 | for (IListenersWithSameMask candidateGroup : listenerGroups) { // group already exists? | ||
135 | if (seedMask.equals(candidateGroup.getSeedMask())) { | ||
136 | return candidateGroup; | ||
137 | } | ||
138 | } | ||
139 | return null; | ||
140 | } | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Represents all listeners subscribed to seeds with the given seed mask. | ||
145 | * | ||
146 | * @author Bergmann Gabor | ||
147 | */ | ||
148 | protected static interface IListenersWithSameMask { | ||
149 | |||
150 | public TupleMask getSeedMask(); | ||
151 | |||
152 | public void deliver(Tuple updateTuple, boolean isInsertion); | ||
153 | |||
154 | public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener); | ||
155 | /** | ||
156 | * @return true if this was the last listener, and the {@link IListenersWithSameMask} can be disposed of. | ||
157 | */ | ||
158 | public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener); | ||
159 | } | ||
160 | /** | ||
161 | * Listeners interested in all tuples | ||
162 | */ | ||
163 | protected final class UniversalListeners implements IListenersWithSameMask { | ||
164 | private final TupleMask mask = TupleMask.empty(inputKey.getArity()); | ||
165 | private List<IQueryRuntimeContextListener> listeners = CollectionsFactory.createObserverList(); | ||
166 | |||
167 | @Override | ||
168 | public TupleMask getSeedMask() { | ||
169 | return mask; | ||
170 | } | ||
171 | @Override | ||
172 | public void deliver(Tuple updateTuple, boolean isInsertion) { | ||
173 | IInputKey key = inputKey; | ||
174 | for (IQueryRuntimeContextListener listener : listeners) { | ||
175 | listener.update(key, updateTuple, isInsertion); | ||
176 | } | ||
177 | } | ||
178 | @Override | ||
179 | public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
180 | listeners.add(listener); | ||
181 | } | ||
182 | @Override | ||
183 | public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
184 | listeners.remove(listener); | ||
185 | return listeners.isEmpty(); | ||
186 | } | ||
187 | } | ||
188 | /** | ||
189 | * Listeners interested in all tuples seeded by a single columns | ||
190 | */ | ||
191 | protected final class ColumnBoundListeners implements IListenersWithSameMask { | ||
192 | private int seedPosition; | ||
193 | protected final TupleMask mask; | ||
194 | // indexed by projected seed tuple | ||
195 | protected IMultiLookup<Object,IQueryRuntimeContextListener> listeners = | ||
196 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
197 | |||
198 | public ColumnBoundListeners(int seedPosition) { | ||
199 | this.seedPosition = seedPosition; | ||
200 | this.mask = TupleMask.selectSingle(seedPosition, inputKey.getArity()); | ||
201 | } | ||
202 | |||
203 | @Override | ||
204 | public TupleMask getSeedMask() { | ||
205 | return mask; | ||
206 | } | ||
207 | @Override | ||
208 | public void deliver(Tuple updateTuple, boolean isInsertion) { | ||
209 | IInputKey key = inputKey; | ||
210 | Object projectedSeed = updateTuple.get(seedPosition); | ||
211 | for (IQueryRuntimeContextListener listener : listeners.lookupOrEmpty(projectedSeed)) { | ||
212 | listener.update(key, updateTuple, isInsertion); | ||
213 | } | ||
214 | } | ||
215 | @Override | ||
216 | public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
217 | Object projectedSeed = originalSeed.get(seedPosition); | ||
218 | listeners.addPair(projectedSeed, listener); | ||
219 | } | ||
220 | @Override | ||
221 | public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
222 | Object projectedSeed = originalSeed.get(seedPosition); | ||
223 | listeners.removePair(projectedSeed, listener); | ||
224 | return listeners.countKeys() == 0; | ||
225 | } | ||
226 | } | ||
227 | /** | ||
228 | * Listeners interested in all tuples seeded by a tuple of values | ||
229 | */ | ||
230 | protected final class GenericBoundListeners implements IListenersWithSameMask { | ||
231 | protected final TupleMask mask; | ||
232 | // indexed by projected seed tuple | ||
233 | protected IMultiLookup<Tuple,IQueryRuntimeContextListener> listeners = | ||
234 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
235 | |||
236 | public GenericBoundListeners(TupleMask mask) { | ||
237 | this.mask = mask; | ||
238 | } | ||
239 | |||
240 | @Override | ||
241 | public TupleMask getSeedMask() { | ||
242 | return mask; | ||
243 | } | ||
244 | @Override | ||
245 | public void deliver(Tuple updateTuple, boolean isInsertion) { | ||
246 | IInputKey key = inputKey; | ||
247 | Tuple projectedSeed = mask.transform(updateTuple); | ||
248 | for (IQueryRuntimeContextListener listener : listeners.lookupOrEmpty(projectedSeed)) { | ||
249 | listener.update(key, updateTuple, isInsertion); | ||
250 | } | ||
251 | } | ||
252 | @Override | ||
253 | public void addUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
254 | Tuple projectedSeed = mask.transform(originalSeed); | ||
255 | listeners.addPair(projectedSeed, listener); | ||
256 | } | ||
257 | @Override | ||
258 | public boolean removeUpdateListener(Tuple originalSeed, IQueryRuntimeContextListener listener) { | ||
259 | Tuple projectedSeed = mask.transform(originalSeed); | ||
260 | listeners.removePair(projectedSeed, listener); | ||
261 | return listeners.countKeys() == 0; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | |||
266 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java deleted file mode 100644 index 39475d11..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DefaultIndexTable.java +++ /dev/null | |||
@@ -1,143 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.IMemory; | ||
25 | |||
26 | /** | ||
27 | * Demo default implementation. | ||
28 | * <p> | ||
29 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
30 | * part of a work in progress. There is no guarantee that this API will | ||
31 | * work or that it will remain the same. | ||
32 | * | ||
33 | * @since 2.0 | ||
34 | * @author Gabor Bergmann | ||
35 | */ | ||
36 | public class DefaultIndexTable extends AbstractIndexTable implements ITableWriterGeneric { | ||
37 | |||
38 | protected IMemory<Tuple> rows = CollectionsFactory.createMultiset(); // TODO use SetMemory if unique | ||
39 | protected Map<TupleMask, MaskedTupleMemory<?>> indexMemories = CollectionsFactory.createMap(); | ||
40 | private boolean unique; | ||
41 | |||
42 | /** | ||
43 | * @param unique | ||
44 | * client promises to only insert a given tuple with multiplicity one | ||
45 | */ | ||
46 | public DefaultIndexTable(IInputKey inputKey, ITableContext tableContext, boolean unique) { | ||
47 | super(inputKey, tableContext); | ||
48 | this.unique = unique; | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void write(Direction direction, Tuple row) { | ||
53 | if (direction == Direction.INSERT) { | ||
54 | boolean changed = rows.addOne(row); | ||
55 | if (unique && !changed) { | ||
56 | String msg = String.format( | ||
57 | "Error: trying to add duplicate row %s to the unique table %s. This indicates some errors in underlying model representation.", | ||
58 | row, getInputKey().getPrettyPrintableName()); | ||
59 | logError(msg); | ||
60 | } | ||
61 | if (changed) { | ||
62 | for (MaskedTupleMemory<?> indexMemory : indexMemories.values()) { | ||
63 | indexMemory.add(row); | ||
64 | } | ||
65 | if (emitNotifications) { | ||
66 | deliverChangeNotifications(row, true); | ||
67 | } | ||
68 | } | ||
69 | } else { // DELETE | ||
70 | boolean changed = rows.removeOne(row); | ||
71 | if (unique && !changed) { | ||
72 | String msg = String.format( | ||
73 | "Error: trying to remove duplicate value %s from the unique table %s. This indicates some errors in underlying model representation.", | ||
74 | row, getInputKey().getPrettyPrintableName()); | ||
75 | logError(msg); | ||
76 | } | ||
77 | if (changed) { | ||
78 | for (MaskedTupleMemory<?> indexMemory : indexMemories.values()) { | ||
79 | indexMemory.remove(row); | ||
80 | } | ||
81 | if (emitNotifications) { | ||
82 | deliverChangeNotifications(row, false); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public boolean containsTuple(ITuple seed) { | ||
90 | return rows.distinctValues().contains(seed); | ||
91 | } | ||
92 | |||
93 | private MaskedTupleMemory<?> getIndexMemory(TupleMask seedMask) { | ||
94 | return indexMemories.computeIfAbsent(seedMask, mask -> { | ||
95 | MaskedTupleMemory<?> memory = MaskedTupleMemory.create(seedMask, MemoryType.SETS, DefaultIndexTable.this); | ||
96 | for (Tuple tuple : rows.distinctValues()) { | ||
97 | memory.add(tuple); | ||
98 | } | ||
99 | return memory; | ||
100 | }); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public int countTuples(TupleMask seedMask, ITuple seed) { | ||
105 | switch (seedMask.getSize()) { | ||
106 | case 0: // unseeded | ||
107 | return rows.size(); | ||
108 | default: | ||
109 | return getIndexMemory(seedMask).getOrEmpty(seed).size(); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
115 | // always exact count | ||
116 | if (groupMask.getSize() == 0) { | ||
117 | return rows.size() == 0 ? Optional.of(0L) : Optional.of(1L); | ||
118 | } else if (groupMask.getSize() == this.emptyTuple.getSize()) { | ||
119 | return Optional.of((long) rows.size()); | ||
120 | } else { | ||
121 | return Optional.of((long)getIndexMemory(groupMask).getKeysetSize()); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public Iterable<Tuple> enumerateTuples(TupleMask seedMask, ITuple seed) { | ||
127 | return getIndexMemory(seedMask).getOrEmpty(seed); | ||
128 | } | ||
129 | |||
130 | @Override | ||
131 | public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) { | ||
132 | return getIndexMemory(seedMask).getOrEmpty(seed).stream(); | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) { | ||
137 | // we assume there is a single omitted index in the mask | ||
138 | int queriedColumn = seedMask.getFirstOmittedIndex().getAsInt(); | ||
139 | return getIndexMemory(seedMask).getOrEmpty(seed).stream() | ||
140 | .map(row2 -> row2.get(queriedColumn)); | ||
141 | } | ||
142 | |||
143 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java deleted file mode 100644 index a3a65f14..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/DisjointUnionTable.java +++ /dev/null | |||
@@ -1,192 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Objects; | ||
15 | import java.util.Optional; | ||
16 | import java.util.stream.Stream; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
26 | |||
27 | /** | ||
28 | * Disjoint union of the provided child tables. | ||
29 | * | ||
30 | * Used e.g. to present a transitive instance table as a view composed from direct instance tables. | ||
31 | * <p> | ||
32 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
33 | * part of a work in progress. There is no guarantee that this API will | ||
34 | * work or that it will remain the same. | ||
35 | * | ||
36 | * @since 2.0 | ||
37 | * @author Gabor Bergmann | ||
38 | */ | ||
39 | public class DisjointUnionTable extends AbstractIndexTable { | ||
40 | |||
41 | protected List<IIndexTable> childTables = CollectionsFactory.createObserverList(); | ||
42 | |||
43 | public DisjointUnionTable(IInputKey inputKey, ITableContext tableContext) { | ||
44 | super(inputKey, tableContext); | ||
45 | } | ||
46 | |||
47 | public List<IIndexTable> getChildTables() { | ||
48 | return Collections.unmodifiableList(childTables); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Precondition: the new child currently is, and will forever stay, disjoint from any other child tables. | ||
53 | */ | ||
54 | public void addChildTable(IIndexTable child) { | ||
55 | if (getInputKey().getArity() != child.getInputKey().getArity()) | ||
56 | throw new IllegalArgumentException(child.toString()); | ||
57 | |||
58 | childTables.add(child); | ||
59 | |||
60 | if (emitNotifications) { | ||
61 | for (Tuple tuple : child.enumerateTuples(emptyMask, Tuples.staticArityFlatTupleOf())) { | ||
62 | deliverChangeNotifications(tuple, true); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public int countTuples(TupleMask seedMask, ITuple seed) { | ||
69 | int count = 0; | ||
70 | for (IIndexTable child : childTables) { | ||
71 | count += child.countTuples(seedMask, seed); | ||
72 | } | ||
73 | return count; | ||
74 | } | ||
75 | |||
76 | |||
77 | @Override | ||
78 | public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
79 | // exact results for trivial projections | ||
80 | if (groupMask.getSize() == 0) { | ||
81 | for (IIndexTable child : childTables) { | ||
82 | if (0 != child.countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf())) | ||
83 | return Optional.of(1L); | ||
84 | } | ||
85 | return Optional.of(0L); | ||
86 | } else if (groupMask.getSize() == emptyTuple.getSize()) { | ||
87 | return Optional.of((long)countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf())); | ||
88 | } | ||
89 | // summing child tables is an upper bound | ||
90 | if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { | ||
91 | return Optional.of((long)countTuples(this.emptyMask, Tuples.staticArityFlatTupleOf())); | ||
92 | } else { // (Accuracy.BEST_LOWER_BOUND == requiredAccuracy) | ||
93 | //projections of child tables may coincide, but the largest one is still a lower bound | ||
94 | Optional<Long> maxProjection = Optional.empty(); | ||
95 | for (IIndexTable child : childTables) { | ||
96 | Optional<Long> estimateOfChild = child.estimateProjectionSize(groupMask, requiredAccuracy); | ||
97 | if (estimateOfChild.isPresent()) { | ||
98 | maxProjection = Optional.of(Math.max(estimateOfChild.get(), maxProjection.orElse(0L))); | ||
99 | } | ||
100 | } | ||
101 | return maxProjection; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) { | ||
107 | Stream<? extends Tuple> stream = Stream.empty(); | ||
108 | for (IIndexTable child : childTables) { | ||
109 | Stream<? extends Tuple> childStream = child.streamTuples(seedMask, seed); | ||
110 | stream = Stream.concat(stream, childStream); | ||
111 | } | ||
112 | return stream; | ||
113 | } | ||
114 | |||
115 | @Override | ||
116 | public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) { | ||
117 | Stream<? extends Object> stream = Stream.empty(); | ||
118 | for (IIndexTable child : childTables) { | ||
119 | Stream<? extends Object> childStream = child.streamValues(seedMask, seed); | ||
120 | stream = Stream.concat(stream, childStream); | ||
121 | } | ||
122 | return stream; | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public boolean containsTuple(ITuple seed) { | ||
127 | for (IIndexTable child : childTables) { | ||
128 | if (child.containsTuple(seed)) | ||
129 | return true; | ||
130 | } | ||
131 | return false; | ||
132 | } | ||
133 | |||
134 | @Override | ||
135 | public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) { | ||
136 | super.addUpdateListener(seed, listener); | ||
137 | |||
138 | for (IIndexTable table : childTables) { | ||
139 | table.addUpdateListener(seed, new ListenerWrapper(listener)); | ||
140 | } | ||
141 | } | ||
142 | @Override | ||
143 | public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener) { | ||
144 | super.removeUpdateListener(seed, listener); | ||
145 | |||
146 | for (IIndexTable table : childTables) { | ||
147 | table.removeUpdateListener(seed, new ListenerWrapper(listener)); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | |||
152 | // TODO this would not be necessary | ||
153 | // if we moved from IQRCL to an interface that does not expose the input key | ||
154 | private class ListenerWrapper implements IQueryRuntimeContextListener { | ||
155 | |||
156 | private IQueryRuntimeContextListener wrappedListener; | ||
157 | public ListenerWrapper(IQueryRuntimeContextListener wrappedListener) { | ||
158 | this.wrappedListener = wrappedListener; | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public void update(IInputKey key, Tuple updateTuple, boolean isInsertion) { | ||
163 | wrappedListener.update(getInputKey(), updateTuple, isInsertion); | ||
164 | } | ||
165 | |||
166 | @Override | ||
167 | public int hashCode() { | ||
168 | return Objects.hash(wrappedListener, DisjointUnionTable.this); | ||
169 | } | ||
170 | @Override | ||
171 | public boolean equals(Object obj) { | ||
172 | if (this == obj) | ||
173 | return true; | ||
174 | if (obj == null) | ||
175 | return false; | ||
176 | if (getClass() != obj.getClass()) | ||
177 | return false; | ||
178 | ListenerWrapper other = (ListenerWrapper) obj; | ||
179 | if (!getOuterType().equals(other.getOuterType())) | ||
180 | return false; | ||
181 | return Objects.equals(wrappedListener, other.wrappedListener); | ||
182 | } | ||
183 | private DisjointUnionTable getOuterType() { | ||
184 | return DisjointUnionTable.this; | ||
185 | } | ||
186 | @Override | ||
187 | public String toString() { | ||
188 | return "Wrapper to DisjointUnion("+getInputKey().getPrettyPrintableName()+") for " + wrappedListener; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java deleted file mode 100644 index be375393..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/IIndexTable.java +++ /dev/null | |||
@@ -1,212 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
23 | |||
24 | /** | ||
25 | * Read-only interface that provides the {@link IInputKey}-specific slice of an instance store to realize a | ||
26 | * {@link IQueryRuntimeContext}. Implemented by a customizable data store that is responsible for: | ||
27 | * <ul> | ||
28 | * <li>storing the instance tuples of the {@link IInputKey},</li> | ||
29 | * <li>providing efficient lookup via storage-specific indexing,</li> | ||
30 | * <li>delivering notifications. (TODO not designed yet)</li> | ||
31 | * </ul> | ||
32 | * | ||
33 | * <p> | ||
34 | * Can be specialized for unary / binary / etc., opposite edges or node subtypes, specific types, distributed storage, | ||
35 | * etc. | ||
36 | * <p> | ||
37 | * Writeable API is specific to the customized implementations (e.g. unary). | ||
38 | * | ||
39 | * <p> | ||
40 | * <b>Precondition:</b> the associated input key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
41 | * <p> | ||
42 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
43 | * part of a work in progress. There is no guarantee that this API will | ||
44 | * work or that it will remain the same. | ||
45 | * | ||
46 | * @since 2.0 | ||
47 | * @author Gabor Bergmann | ||
48 | * @noimplement This interface is not intended to be implemented directly. Extend {@link AbstractIndexTable} instead. | ||
49 | */ | ||
50 | public interface IIndexTable { | ||
51 | |||
52 | // TODO add superinterface that represents a statistics-only counter? | ||
53 | |||
54 | /** | ||
55 | * @return the input key indexed by this table | ||
56 | */ | ||
57 | public IInputKey getInputKey(); | ||
58 | |||
59 | /** | ||
60 | * Returns the tuples, optionally seeded with the given tuple. | ||
61 | * | ||
62 | * <p> Consider using the more idiomatic {@link #streamTuples(TupleMask, ITuple)} instead. | ||
63 | * | ||
64 | * @param seedMask | ||
65 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
66 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
67 | * once in seedMask. | ||
68 | * @param seed | ||
69 | * the tuple of fixed values restricting the row set to be considered, in the same order as given in | ||
70 | * parameterSeedMask, so that for each considered row tuple, | ||
71 | * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null. | ||
72 | * @return the tuples in the table for the given key and seed | ||
73 | */ | ||
74 | @SuppressWarnings("unchecked") | ||
75 | public default Iterable<Tuple> enumerateTuples(TupleMask seedMask, ITuple seed) { | ||
76 | return () -> (Iterator<Tuple>) (streamTuples(seedMask, seed).iterator()); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Returns the tuples, optionally seeded with the given tuple. | ||
81 | * | ||
82 | * @param seedMask | ||
83 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
84 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
85 | * once in seedMask. | ||
86 | * @param seed | ||
87 | * the tuple of fixed values restricting the row set to be considered, in the same order as given in | ||
88 | * parameterSeedMask, so that for each considered row tuple, | ||
89 | * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null. | ||
90 | * @return the tuples in the table for the given key and seed | ||
91 | * @since 2.1 | ||
92 | */ | ||
93 | public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed); | ||
94 | |||
95 | /** | ||
96 | * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound | ||
97 | * by the seed except for one. | ||
98 | * | ||
99 | * <p> | ||
100 | * Selects the tuples in the table, optionally seeded with the given tuple, and then returns the single value from | ||
101 | * each tuple which is not bound by the seed mask. | ||
102 | * | ||
103 | * <p> Consider using the more idiomatic {@link #streamValues(TupleMask, ITuple)} instead. | ||
104 | * | ||
105 | * @param seedMask | ||
106 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
107 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
108 | * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. | ||
109 | * @param seed | ||
110 | * the tuple of fixed values restricting the row set to be considered, in the same order as given in | ||
111 | * parameterSeedMask, so that for each considered row tuple, | ||
112 | * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null. | ||
113 | * @return the objects in the table for the given key and seed | ||
114 | * | ||
115 | */ | ||
116 | @SuppressWarnings("unchecked") | ||
117 | public default Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) { | ||
118 | return () -> (Iterator<Object>) (streamValues(seedMask, seed).iterator()); | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound | ||
123 | * by the seed except for one. | ||
124 | * | ||
125 | * <p> | ||
126 | * Selects the tuples in the table, optionally seeded with the given tuple, and then returns the single value from | ||
127 | * each tuple which is not bound by the seed mask. | ||
128 | * | ||
129 | * @param seedMask | ||
130 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
131 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
132 | * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. | ||
133 | * @param seed | ||
134 | * the tuple of fixed values restricting the row set to be considered, in the same order as given in | ||
135 | * parameterSeedMask, so that for each considered row tuple, | ||
136 | * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null. | ||
137 | * @return the objects in the table for the given key and seed | ||
138 | * | ||
139 | * @since 2.1 | ||
140 | */ | ||
141 | public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed); | ||
142 | |||
143 | /** | ||
144 | * Simpler form of {@link #enumerateTuples(TupleMask, ITuple)} in the case where all values of the tuples are bound | ||
145 | * by the seed. | ||
146 | * | ||
147 | * <p> | ||
148 | * Returns whether the given tuple is in the table identified by the input key. | ||
149 | * | ||
150 | * @param seed | ||
151 | * a row tuple of fixed values whose presence in the table is queried | ||
152 | * @return true iff there is a row tuple contained in the table that corresponds to the given seed | ||
153 | */ | ||
154 | public boolean containsTuple(ITuple seed); | ||
155 | |||
156 | /** | ||
157 | * Returns the number of tuples, optionally seeded with the given tuple. | ||
158 | * | ||
159 | * <p> | ||
160 | * Selects the tuples in the table, optionally seeded with the given tuple, and then returns their number. | ||
161 | * | ||
162 | * @param seedMask | ||
163 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
164 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
165 | * once in seedMask. | ||
166 | * @param seed | ||
167 | * the tuple of fixed values restricting the row set to be considered, in the same order as given in | ||
168 | * parameterSeedMask, so that for each considered row tuple, | ||
169 | * projectedParameterSeed.equals(parameterSeedMask.transform(row)) should hold. Must not be null. | ||
170 | * @return the number of tuples in the table for the given key and seed | ||
171 | * | ||
172 | */ | ||
173 | public int countTuples(TupleMask seedMask, ITuple seed); | ||
174 | |||
175 | /** | ||
176 | * Gives an estimate of the number of different groups the tuples of the table are projected into by the given mask | ||
177 | * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. | ||
178 | * | ||
179 | * <p> Derived tables may return {@link Optional#empty()} if it would be costly to provide an answer up to the required precision. | ||
180 | * Direct storage tables are expected to always be able to give an exact count. | ||
181 | * | ||
182 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
183 | * | ||
184 | * @since 2.1 | ||
185 | */ | ||
186 | public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy); | ||
187 | |||
188 | /** | ||
189 | * Subscribes for updates in the table, optionally seeded with the given tuple. | ||
190 | * <p> This should be called after initializing a result cache by an enumeration method. | ||
191 | * | ||
192 | * @param seed can be null or a tuple with matching arity; | ||
193 | * if non-null, notifications will delivered only about those updates of the table | ||
194 | * that match the seed at positions where the seed is non-null. | ||
195 | * @param listener will be notified of future changes | ||
196 | * | ||
197 | * @since 2.1 | ||
198 | */ | ||
199 | public void addUpdateListener(Tuple seed, IQueryRuntimeContextListener listener); | ||
200 | |||
201 | /** | ||
202 | * Unsubscribes from updates in the table, optionally seeded with the given tuple. | ||
203 | * | ||
204 | * @param seed can be null or a tuple with matching arity; | ||
205 | * see {@link #addUpdateListener(Tuple, IQueryRuntimeContextListener)} for definition. | ||
206 | * @param listener will no longer be notified of future changes | ||
207 | * | ||
208 | * @since 2.1 | ||
209 | */ | ||
210 | public void removeUpdateListener(Tuple seed, IQueryRuntimeContextListener listener); | ||
211 | |||
212 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java deleted file mode 100644 index 69e83cd7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableContext.java +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | /** | ||
12 | * Callbacks that {@link IIndexTable} implementations are expected to invoke on their environment. | ||
13 | * <p> | ||
14 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
15 | * part of a work in progress. There is no guarantee that this API will | ||
16 | * work or that it will remain the same. | ||
17 | * | ||
18 | * @since 2.0 | ||
19 | * @author Gabor Bergmann | ||
20 | */ | ||
21 | public interface ITableContext { | ||
22 | |||
23 | // TODO notifications? | ||
24 | |||
25 | /** | ||
26 | * Indicates that an error has occurred in maintaining an index table, e.g. duplicate value. | ||
27 | */ | ||
28 | public void logError(String message); | ||
29 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java deleted file mode 100644 index fb614014..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterBinary.java +++ /dev/null | |||
@@ -1,53 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | |||
14 | /** | ||
15 | * Modifies the contents of a binary {@link IIndexTable}. | ||
16 | * <p> | ||
17 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
18 | * part of a work in progress. There is no guarantee that this API will | ||
19 | * work or that it will remain the same. | ||
20 | * | ||
21 | * @since 2.0 | ||
22 | * @author Gabor Bergmann | ||
23 | */ | ||
24 | public interface ITableWriterBinary<Source, Target> { | ||
25 | /** | ||
26 | * Adds/removes a row to/from the table. | ||
27 | * | ||
28 | * @param direction | ||
29 | * tells whether putting a row into the table or deleting | ||
30 | * | ||
31 | * TODO: store as multiset, return bool? | ||
32 | */ | ||
33 | void write(Direction direction, Source source, Target target); | ||
34 | |||
35 | /** | ||
36 | * Intersection type for writers that are also tables | ||
37 | */ | ||
38 | interface Table<Source, Target> extends ITableWriterBinary<Source, Target>, IIndexTable { | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * /dev/null implementation | ||
43 | * | ||
44 | * @author Gabor Bergmann | ||
45 | */ | ||
46 | static class Nop<Source, Target> implements ITableWriterBinary<Source, Target> { | ||
47 | @Override | ||
48 | public void write(Direction direction, Source source, Target target) { | ||
49 | // NO-OP | ||
50 | } | ||
51 | |||
52 | } | ||
53 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java deleted file mode 100644 index fb1ebcc0..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterGeneric.java +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | |||
14 | /** | ||
15 | * Modifies the contents of an {@link IIndexTable}. | ||
16 | * <p> | ||
17 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
18 | * part of a work in progress. There is no guarantee that this API will | ||
19 | * work or that it will remain the same. | ||
20 | * | ||
21 | * @since 2.0 | ||
22 | * @author Gabor Bergmann | ||
23 | */ | ||
24 | public interface ITableWriterGeneric { | ||
25 | |||
26 | /** | ||
27 | * Adds/removes a row to/from the table. | ||
28 | * | ||
29 | * @param direction | ||
30 | * tells whether putting a row into the table or deleting TODO: store as multiset, return bool? | ||
31 | */ | ||
32 | void write(Direction direction, Tuple row); | ||
33 | |||
34 | /** | ||
35 | * Intersection type for writers that are also tables | ||
36 | */ | ||
37 | interface Table extends ITableWriterGeneric, IIndexTable { | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * /dev/null implementation | ||
42 | * | ||
43 | * @author Gabor Bergmann | ||
44 | */ | ||
45 | static class Nop implements ITableWriterGeneric { | ||
46 | @Override | ||
47 | public void write(Direction direction, Tuple row) { | ||
48 | // NO-OP | ||
49 | } | ||
50 | |||
51 | } | ||
52 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java deleted file mode 100644 index f90d5362..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/ITableWriterUnary.java +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | |||
14 | /** | ||
15 | * Modifies the contents of a unary {@link IIndexTable}. | ||
16 | * <p> | ||
17 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
18 | * part of a work in progress. There is no guarantee that this API will | ||
19 | * work or that it will remain the same. | ||
20 | * | ||
21 | * @since 2.0 | ||
22 | * @author Gabor Bergmann | ||
23 | * | ||
24 | */ | ||
25 | public interface ITableWriterUnary<Value> { | ||
26 | |||
27 | /** | ||
28 | * Adds/removes a row to/from the table. | ||
29 | * | ||
30 | * @param direction | ||
31 | * tells whether putting a row into the table or deleting TODO: store as multiset, return bool? | ||
32 | */ | ||
33 | void write(Direction direction, Value value); | ||
34 | |||
35 | /** | ||
36 | * Intersection type for writers that are also tables | ||
37 | */ | ||
38 | interface Table<Value> extends ITableWriterUnary<Value>, IIndexTable { | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * /dev/null implementation | ||
43 | * | ||
44 | * @author Gabor Bergmann | ||
45 | */ | ||
46 | static class Nop<Value> implements ITableWriterUnary<Value> { | ||
47 | @Override | ||
48 | public void write(Direction direction, Value value) { | ||
49 | // NO-OP | ||
50 | } | ||
51 | } | ||
52 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java deleted file mode 100644 index 588ed9d2..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleBinaryTable.java +++ /dev/null | |||
@@ -1,320 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
28 | |||
29 | /** | ||
30 | * Simple source-target bidirectional mapping. | ||
31 | * | ||
32 | * <p> | ||
33 | * TODO: specialize for to-one features and unique to-many features | ||
34 | * <p> | ||
35 | * TODO: on-demand construction of valueToHolderMap | ||
36 | * <p> | ||
37 | * TODO: support for lean indexing, opposites, long surrogate ids, etc. | ||
38 | * <p> | ||
39 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
40 | * part of a work in progress. There is no guarantee that this API will | ||
41 | * work or that it will remain the same. | ||
42 | * | ||
43 | * @since 2.0 | ||
44 | * @author Gabor Bergmann | ||
45 | */ | ||
46 | public class SimpleBinaryTable<Source, Target> extends AbstractIndexTable | ||
47 | implements ITableWriterBinary.Table<Source, Target> { | ||
48 | |||
49 | /** | ||
50 | * value -> holder(s) | ||
51 | * <p> | ||
52 | * this is currently the primary store, may hold duplicates depending on the unique parameter | ||
53 | */ | ||
54 | private IMultiLookup<Target, Source> valueToHolderMap; | ||
55 | /** | ||
56 | * holder -> value(s); constructed on-demand, null if unused | ||
57 | */ | ||
58 | private IMultiLookup<Source, Target> holderToValueMap; | ||
59 | private int totalRowCount = 0; | ||
60 | private boolean unique; | ||
61 | |||
62 | /** | ||
63 | * @param unique | ||
64 | * client promises to only insert a given tuple with multiplicity one | ||
65 | */ | ||
66 | public SimpleBinaryTable(IInputKey inputKey, ITableContext tableContext, boolean unique) { | ||
67 | super(inputKey, tableContext); | ||
68 | this.unique = unique; | ||
69 | valueToHolderMap = CollectionsFactory.createMultiLookup(Object.class, | ||
70 | unique ? MemoryType.SETS : MemoryType.MULTISETS, Object.class); | ||
71 | if (2 != inputKey.getArity()) | ||
72 | throw new IllegalArgumentException(inputKey.toString()); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public void write(Direction direction, Source holder, Target value) { | ||
77 | if (direction == Direction.INSERT) { | ||
78 | try { | ||
79 | // TODO we currently assume V2H map exists | ||
80 | boolean changed = addToValueToHolderMap(value, holder); | ||
81 | if (holderToValueMap != null) { | ||
82 | addToHolderToValueMap(value, holder); | ||
83 | } | ||
84 | if (changed) { | ||
85 | totalRowCount++; | ||
86 | if (emitNotifications) { | ||
87 | deliverChangeNotifications(Tuples.staticArityFlatTupleOf(holder, value), true); | ||
88 | } | ||
89 | } | ||
90 | } catch (IllegalStateException ex) { // if unique table and duplicate tuple | ||
91 | String msg = String.format( | ||
92 | "Error: trying to add duplicate value %s to the unique feature %s of host object %s. This indicates some errors in underlying model representation.", | ||
93 | value, getInputKey().getPrettyPrintableName(), holder); | ||
94 | logError(msg); | ||
95 | } | ||
96 | } else { // DELETE | ||
97 | try { | ||
98 | // TODO we currently assume V2H map exists | ||
99 | boolean changed = removeFromValueToHolderMap(value, holder); | ||
100 | if (holderToValueMap != null) { | ||
101 | removeFromHolderToValueMap(value, holder); | ||
102 | } | ||
103 | if (changed) { | ||
104 | totalRowCount--; | ||
105 | if (emitNotifications) { | ||
106 | deliverChangeNotifications(Tuples.staticArityFlatTupleOf(holder, value), false); | ||
107 | } | ||
108 | } | ||
109 | } catch (IllegalStateException ex) { // if unique table and duplicate tuple | ||
110 | String msg = String.format( | ||
111 | "Error: trying to remove non-existing value %s from the feature %s of host object %s. This indicates some errors in underlying model representation.", | ||
112 | value, getInputKey().getPrettyPrintableName(), holder); | ||
113 | logError(msg); | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | private boolean addToHolderToValueMap(Target value, Source holder) { | ||
119 | return ChangeGranularity.DUPLICATE != holderToValueMap.addPair(holder, value); | ||
120 | } | ||
121 | |||
122 | private boolean addToValueToHolderMap(Target value, Source holder) { | ||
123 | return ChangeGranularity.DUPLICATE != valueToHolderMap.addPair(value, holder); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * @throws IllegalStateException | ||
128 | */ | ||
129 | private boolean removeFromHolderToValueMap(Target value, Source holder) { | ||
130 | return ChangeGranularity.DUPLICATE != holderToValueMap.removePair(holder, value); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * @throws IllegalStateException | ||
135 | */ | ||
136 | private boolean removeFromValueToHolderMap(Target value, Source holder) { | ||
137 | return ChangeGranularity.DUPLICATE != valueToHolderMap.removePair(value, holder); | ||
138 | } | ||
139 | |||
140 | @SuppressWarnings("unchecked") | ||
141 | @Override | ||
142 | public int countTuples(TupleMask seedMask, ITuple seed) { | ||
143 | switch (seedMask.getSize()) { | ||
144 | case 0: // unseeded | ||
145 | // TODO we currently assume V2H map exists | ||
146 | return totalRowCount; | ||
147 | case 1: // lookup by source or target | ||
148 | int seedIndex = seedMask.indices[0]; | ||
149 | if (seedIndex == 0) { // lookup by source | ||
150 | Source source = (Source) seed.get(0); | ||
151 | return getDistinctValuesOfHolder(source).size(); | ||
152 | } else if (seedIndex == 1) { // lookup by target | ||
153 | Target target = (Target) seed.get(0); | ||
154 | return getDistinctHoldersOfValue(target).size(); | ||
155 | } else | ||
156 | throw new IllegalArgumentException(seedMask.toString()); | ||
157 | case 2: // containment check | ||
158 | // hack: if mask is not identity, then it is [1,0]/2, which is its own inverse | ||
159 | Source source = (Source) seedMask.getValue(seed, 0); | ||
160 | Target target = (Target) seedMask.getValue(seed, 1); | ||
161 | if (containsRow(source, target)) | ||
162 | return 1; | ||
163 | else | ||
164 | return 0; | ||
165 | default: | ||
166 | throw new IllegalArgumentException(seedMask.toString()); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | @Override | ||
171 | public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
172 | // always exact count | ||
173 | if (groupMask.getSize() == 0) { | ||
174 | return totalRowCount == 0 ? Optional.of(0L) : Optional.of(1L); | ||
175 | } else if (groupMask.getSize() == 2) { | ||
176 | return Optional.of((long)totalRowCount); | ||
177 | } else if (groupMask.indices[0] == 0) { // project to holder | ||
178 | return Optional.of((long)getHolderToValueMap().countKeys()); | ||
179 | } else { // (groupMask.indices[0] == 0) // project to value | ||
180 | return Optional.of((long)getValueToHolderMap().countKeys()); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | @Override | ||
185 | public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) { | ||
186 | switch (seedMask.getSize()) { | ||
187 | case 0: // unseeded | ||
188 | // TODO we currently assume V2H map exists | ||
189 | return getAllDistinctValuesStream() | ||
190 | .flatMap(value -> valueToHolderMap.lookup(value).distinctValues().stream() | ||
191 | .map(source -> Tuples.staticArityFlatTupleOf(source, value))); | ||
192 | |||
193 | case 1: // lookup by source or target | ||
194 | int seedIndex = seedMask.indices[0]; | ||
195 | if (seedIndex == 0) { // lookup by source | ||
196 | @SuppressWarnings("unchecked") | ||
197 | Source source = (Source) seed.get(0); | ||
198 | return getDistinctValuesOfHolder(source).stream() | ||
199 | .map(target -> Tuples.staticArityFlatTupleOf(source, target)); | ||
200 | } else if (seedIndex == 1) { // lookup by target | ||
201 | @SuppressWarnings("unchecked") | ||
202 | Target target = (Target) seed.get(0); | ||
203 | return getDistinctHoldersOfValue(target).stream() | ||
204 | .map(source -> Tuples.staticArityFlatTupleOf(source, target)); | ||
205 | } else | ||
206 | throw new IllegalArgumentException(seedMask.toString()); | ||
207 | case 2: // containment check | ||
208 | // hack: if mask is not identity, then it is [1,0]/2, which is its own inverse | ||
209 | @SuppressWarnings("unchecked") | ||
210 | Source source = (Source) seedMask.getValue(seed, 0); | ||
211 | @SuppressWarnings("unchecked") | ||
212 | Target target = (Target) seedMask.getValue(seed, 1); | ||
213 | |||
214 | if (containsRow(source, target)) | ||
215 | return Stream.of(Tuples.staticArityFlatTupleOf(source, target)); | ||
216 | else | ||
217 | return Stream.empty(); | ||
218 | default: | ||
219 | throw new IllegalArgumentException(seedMask.toString()); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | @Override | ||
224 | @SuppressWarnings("unchecked") | ||
225 | public Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) { | ||
226 | if (seedMask.getSize() != 1) | ||
227 | throw new IllegalArgumentException(seedMask.toString()); | ||
228 | |||
229 | int seedIndex = seedMask.indices[0]; | ||
230 | if (seedIndex == 0) { // lookup by source | ||
231 | return getDistinctValuesOfHolder((Source) seed.get(0)); | ||
232 | } else if (seedIndex == 1) { // lookup by target | ||
233 | return getDistinctHoldersOfValue((Target) seed.get(0)); | ||
234 | } else | ||
235 | throw new IllegalArgumentException(seedMask.toString()); | ||
236 | |||
237 | } | ||
238 | @Override | ||
239 | public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) { | ||
240 | if (seedMask.getSize() != 1) | ||
241 | throw new IllegalArgumentException(seedMask.toString()); | ||
242 | |||
243 | int seedIndex = seedMask.indices[0]; | ||
244 | if (seedIndex == 0) { // lookup by source | ||
245 | return getDistinctValuesOfHolder((Source) seed.get(0)).stream(); | ||
246 | } else if (seedIndex == 1) { // lookup by target | ||
247 | return getDistinctHoldersOfValue((Target) seed.get(0)).stream(); | ||
248 | } else | ||
249 | throw new IllegalArgumentException(seedMask.toString()); | ||
250 | |||
251 | } | ||
252 | |||
253 | @Override | ||
254 | @SuppressWarnings("unchecked") | ||
255 | public boolean containsTuple(ITuple seed) { | ||
256 | return containsRow((Source) seed.get(0), (Target) seed.get(1)); | ||
257 | } | ||
258 | |||
259 | public boolean containsRow(Source source, Target target) { | ||
260 | // TODO we currently assume V2H map exists | ||
261 | if (valueToHolderMap != null) { | ||
262 | IMemoryView<Source> holders = valueToHolderMap.lookup(target); | ||
263 | return holders != null && holders.containsNonZero(source); | ||
264 | } else | ||
265 | throw new UnsupportedOperationException("TODO implement"); | ||
266 | } | ||
267 | |||
268 | public Iterable<Source> getAllDistinctHolders() { | ||
269 | return getHolderToValueMap().distinctKeys(); | ||
270 | } | ||
271 | public Stream<Source> getAllDistinctHoldersStream() { | ||
272 | return getHolderToValueMap().distinctKeysStream(); | ||
273 | } | ||
274 | public Iterable<Target> getAllDistinctValues() { | ||
275 | return getValueToHolderMap().distinctKeys(); | ||
276 | } | ||
277 | public Stream<Target> getAllDistinctValuesStream() { | ||
278 | return getValueToHolderMap().distinctKeysStream(); | ||
279 | } | ||
280 | |||
281 | public Set<Source> getDistinctHoldersOfValue(Target value) { | ||
282 | IMemoryView<Source> holdersMultiset = getValueToHolderMap().lookup(value); | ||
283 | if (holdersMultiset == null) | ||
284 | return Collections.emptySet(); | ||
285 | else | ||
286 | return holdersMultiset.distinctValues(); | ||
287 | } | ||
288 | |||
289 | public Set<Target> getDistinctValuesOfHolder(Source holder) { | ||
290 | IMemoryView<Target> valuesMultiset = getHolderToValueMap().lookup(holder); | ||
291 | if (valuesMultiset == null) | ||
292 | return Collections.emptySet(); | ||
293 | else | ||
294 | return valuesMultiset.distinctValues(); | ||
295 | } | ||
296 | |||
297 | private IMultiLookup<Source, Target> getHolderToValueMap() { | ||
298 | if (holderToValueMap == null) { | ||
299 | holderToValueMap = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, // no duplicates, as | ||
300 | // this is the | ||
301 | // secondary | ||
302 | // collection | ||
303 | Object.class); | ||
304 | |||
305 | // TODO we currently assume V2H map exists | ||
306 | for (Target value : valueToHolderMap.distinctKeys()) { | ||
307 | for (Source holder : valueToHolderMap.lookup(value).distinctValues()) { | ||
308 | holderToValueMap.addPair(holder, value); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | return holderToValueMap; | ||
313 | } | ||
314 | |||
315 | private IMultiLookup<Target, Source> getValueToHolderMap() { | ||
316 | // TODO we currently assume V2H map exists | ||
317 | return valueToHolderMap; | ||
318 | } | ||
319 | |||
320 | } | ||
diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java deleted file mode 100644 index 428ea78b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/scopes/tables/SimpleUnaryTable.java +++ /dev/null | |||
@@ -1,140 +0,0 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.matchers.scopes.tables; | ||
11 | |||
12 | import java.util.Optional; | ||
13 | import java.util.stream.Stream; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMemory; | ||
24 | |||
25 | /** | ||
26 | * Simple value set. | ||
27 | * <p> | ||
28 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
29 | * part of a work in progress. There is no guarantee that this API will | ||
30 | * work or that it will remain the same. | ||
31 | * | ||
32 | * @since 2.0 | ||
33 | * @author Gabor Bergmann | ||
34 | */ | ||
35 | public class SimpleUnaryTable<Value> extends AbstractIndexTable implements ITableWriterUnary.Table<Value> { | ||
36 | |||
37 | protected IMemory<Value> values = CollectionsFactory.createMultiset(); // TODO use SetMemory if unique | ||
38 | |||
39 | private boolean unique; | ||
40 | |||
41 | /** | ||
42 | * @param unique | ||
43 | * client promises to only insert a given tuple with multiplicity one | ||
44 | */ | ||
45 | public SimpleUnaryTable(IInputKey inputKey, ITableContext tableContext, boolean unique) { | ||
46 | super(inputKey, tableContext); | ||
47 | this.unique = unique; | ||
48 | if (1 != inputKey.getArity()) | ||
49 | throw new IllegalArgumentException(inputKey.toString()); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void write(Direction direction, Value value) { | ||
54 | if (direction == Direction.INSERT) { | ||
55 | boolean changed = values.addOne(value); | ||
56 | if (unique && !changed) { | ||
57 | String msg = String.format( | ||
58 | "Error: trying to add duplicate value %s to the unique set %s. This indicates some errors in underlying model representation.", | ||
59 | value, getInputKey().getPrettyPrintableName()); | ||
60 | logError(msg); | ||
61 | } | ||
62 | if (changed && emitNotifications) { | ||
63 | deliverChangeNotifications(Tuples.staticArityFlatTupleOf(value), true); | ||
64 | } | ||
65 | } else { // DELETE | ||
66 | boolean changed = values.removeOne(value); | ||
67 | if (unique && !changed) { | ||
68 | String msg = String.format( | ||
69 | "Error: trying to remove duplicate value %s from the unique set %s. This indicates some errors in underlying model representation.", | ||
70 | value, getInputKey().getPrettyPrintableName()); | ||
71 | logError(msg); | ||
72 | } | ||
73 | if (changed && emitNotifications) { | ||
74 | deliverChangeNotifications(Tuples.staticArityFlatTupleOf(value), false); | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | @SuppressWarnings("unchecked") | ||
81 | public boolean containsTuple(ITuple seed) { | ||
82 | return values.containsNonZero((Value) seed.get(0)); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public int countTuples(TupleMask seedMask, ITuple seed) { | ||
87 | if (seedMask.getSize() == 0) { // unseeded | ||
88 | return values.size(); | ||
89 | } else { | ||
90 | @SuppressWarnings("unchecked") | ||
91 | Value value = (Value) seed.get(0); | ||
92 | return values.containsNonZero(value) ? 1 : 0; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | |||
97 | @Override | ||
98 | public Optional<Long> estimateProjectionSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
99 | // always exact count | ||
100 | if (groupMask.getSize() == 0) { | ||
101 | return values.isEmpty() ? Optional.of(0L) : Optional.of(1L); | ||
102 | } else { | ||
103 | return Optional.of((long)values.size()); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | |||
108 | @Override | ||
109 | public Stream<? extends Tuple> streamTuples(TupleMask seedMask, ITuple seed) { | ||
110 | if (seedMask.getSize() == 0) { // unseeded | ||
111 | return values.distinctValues().stream().map(Tuples::staticArityFlatTupleOf); | ||
112 | } else { | ||
113 | @SuppressWarnings("unchecked") | ||
114 | Value value = (Value) seed.get(0); | ||
115 | if (values.containsNonZero(value)) | ||
116 | return Stream.of(Tuples.staticArityFlatTupleOf(value)); | ||
117 | else | ||
118 | return Stream.empty(); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public Iterable<? extends Object> enumerateValues(TupleMask seedMask, ITuple seed) { | ||
124 | if (seedMask.getSize() == 0) { // unseeded | ||
125 | return values; | ||
126 | } else { | ||
127 | throw new IllegalArgumentException(seedMask.toString()); | ||
128 | } | ||
129 | } | ||
130 | @Override | ||
131 | public Stream<? extends Object> streamValues(TupleMask seedMask, ITuple seed) { | ||
132 | if (seedMask.getSize() == 0) { // unseeded | ||
133 | return values.asStream(); | ||
134 | } else { | ||
135 | throw new IllegalArgumentException(seedMask.toString()); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | |||
140 | } | ||