diff options
Diffstat (limited to 'subprojects/viatra-runtime/src/main/java/tools')
250 files changed, 23933 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java new file mode 100644 index 00000000..a2ae41e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java | |||
@@ -0,0 +1,13 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime; | ||
7 | |||
8 | @FunctionalInterface | ||
9 | public interface CancellationToken { | ||
10 | CancellationToken NONE = () -> {}; | ||
11 | |||
12 | void checkCancelled(); | ||
13 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java new file mode 100644 index 00000000..32a3430d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java | |||
@@ -0,0 +1,363 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
12 | import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.*; | ||
15 | |||
16 | import java.lang.reflect.InvocationTargetException; | ||
17 | import java.util.concurrent.Callable; | ||
18 | |||
19 | /** | ||
20 | * Advanced interface to a VIATRA incremental evaluation engine. | ||
21 | * | ||
22 | * <p> | ||
23 | * You can create a new, private, unmanaged {@link AdvancedViatraQueryEngine} instance using | ||
24 | * {@link #createUnmanagedEngine(QueryScope)}. Additionally, you can access the advanced interface on any | ||
25 | * {@link ViatraQueryEngine} by {@link AdvancedViatraQueryEngine#from(ViatraQueryEngine)}. | ||
26 | * | ||
27 | * <p> | ||
28 | * While the default interface {@link ViatraQueryEngine}, is suitable for most users, this advanced interface provides more | ||
29 | * control over the engine. The most important added functionality is the following: | ||
30 | * <ul> | ||
31 | * <li>You can have tighter control over the lifecycle of the engine, if you create a private, unmanaged engine | ||
32 | * instance. For instance, a (non-managed) engine can be disposed in order to detach from the EMF model and stop | ||
33 | * listening on update notifications. The indexes built previously in the engine can then be garbage collected, even if | ||
34 | * the model itself is retained. Total lifecycle control is only available for private, unmanaged engines (created using | ||
35 | * {@link #createUnmanagedEngine(QueryScope)}); a managed engine (obtained via {@link ViatraQueryEngine#on(QueryScope)}) is | ||
36 | * shared among clients and can not be disposed or wiped. | ||
37 | * <li>You can add and remove listeners to receive notification when the model or the match sets change. | ||
38 | * <li>You can add and remove listeners to receive notification on engine lifecycle events, such as creation of new | ||
39 | * matchers. For instance, if you explicitly share a private, unmanaged engine between multiple sites, you should | ||
40 | * register a callback using {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client | ||
41 | * has called the destructive methods {@link #dispose()} or {@link #wipe()}. | ||
42 | * </ul> | ||
43 | * | ||
44 | * @author Bergmann Gabor | ||
45 | * @noextend This class is not intended to be subclassed by clients. | ||
46 | */ | ||
47 | public abstract class AdvancedViatraQueryEngine extends ViatraQueryEngine { | ||
48 | |||
49 | /** | ||
50 | * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
51 | * | ||
52 | * <p> Repeated invocations will return different instances, so other clients are unable to independently access | ||
53 | * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements | ||
54 | * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed | ||
55 | * engine instance. | ||
56 | * | ||
57 | * <p> | ||
58 | * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface | ||
59 | * {@link AdvancedViatraQueryEngine}. | ||
60 | * | ||
61 | * <p> | ||
62 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
63 | * | ||
64 | * @param scope | ||
65 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
66 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
67 | * @return the advanced interface to a newly created unmanaged engine | ||
68 | * @since 0.9 | ||
69 | */ | ||
70 | public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope) { | ||
71 | return new ViatraQueryEngineImpl(null, scope); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
76 | * | ||
77 | * <p> Repeated invocations will return different instances, so other clients are unable to independently access | ||
78 | * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements | ||
79 | * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed | ||
80 | * engine instance. | ||
81 | * | ||
82 | * <p> | ||
83 | * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface | ||
84 | * {@link AdvancedViatraQueryEngine}. | ||
85 | * | ||
86 | * <p> | ||
87 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
88 | * | ||
89 | * @param scope | ||
90 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
91 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
92 | * @return the advanced interface to a newly created unmanaged engine | ||
93 | * @since 1.4 | ||
94 | */ | ||
95 | public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope, ViatraQueryEngineOptions options) { | ||
96 | return new ViatraQueryEngineImpl(null, scope, options); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Provides access to a given existing engine through the advanced interface. | ||
101 | * | ||
102 | * <p> | ||
103 | * Caveat: if the referenced engine is managed (i.e. created via {@link ViatraQueryEngine#on(QueryScope)}), the advanced | ||
104 | * methods {@link #dispose()} and {@link #wipe()} will not be allowed. | ||
105 | * | ||
106 | * @param engine | ||
107 | * the engine to access using the advanced interface | ||
108 | * @return a reference to the same engine conforming to the advanced interface | ||
109 | */ | ||
110 | public static AdvancedViatraQueryEngine from(ViatraQueryEngine engine) { | ||
111 | return (AdvancedViatraQueryEngine) engine; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Add an engine lifecycle listener to this engine instance. | ||
116 | * | ||
117 | * @param listener | ||
118 | * the {@link ViatraQueryEngineLifecycleListener} that should listen to lifecycle events from this engine | ||
119 | */ | ||
120 | public abstract void addLifecycleListener(ViatraQueryEngineLifecycleListener listener); | ||
121 | |||
122 | /** | ||
123 | * Remove an existing lifecycle listener from this engine instance. | ||
124 | * | ||
125 | * @param listener | ||
126 | * the {@link ViatraQueryEngineLifecycleListener} that should not listen to lifecycle events from this | ||
127 | * engine anymore | ||
128 | */ | ||
129 | public abstract void removeLifecycleListener(ViatraQueryEngineLifecycleListener listener); | ||
130 | |||
131 | /** | ||
132 | * Add an model update event listener to this engine instance (that fires its callbacks according to its | ||
133 | * notification level). | ||
134 | * | ||
135 | * @param listener | ||
136 | * the {@link ViatraQueryModelUpdateListener} that should listen to model update events from this engine. | ||
137 | */ | ||
138 | public abstract void addModelUpdateListener(ViatraQueryModelUpdateListener listener); | ||
139 | |||
140 | /** | ||
141 | * Remove an existing model update event listener to this engine instance. | ||
142 | * | ||
143 | * @param listener | ||
144 | * the {@link ViatraQueryModelUpdateListener} that should not listen to model update events from this engine | ||
145 | * anymore | ||
146 | */ | ||
147 | public abstract void removeModelUpdateListener(ViatraQueryModelUpdateListener listener); | ||
148 | |||
149 | /** | ||
150 | * Registers low-level callbacks for match appearance and disappearance on this pattern matcher. | ||
151 | * | ||
152 | * <p> | ||
153 | * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a | ||
154 | * consistent state yet. Importantly, no model modification permitted during the callback. Most users should use the | ||
155 | * databinding support ({@link org.eclipse.viatra.addon.databinding.runtime.api.ViatraObservables ViatraObservables}) or the event-driven API | ||
156 | * ({@link org.eclipse.viatra.transformation.evm.api.EventDrivenVM EventDrivenVM}) instead. | ||
157 | * | ||
158 | * <p> | ||
159 | * Performance note: expected to be much more efficient than polling at {@link #addCallbackAfterUpdates(Runnable)}, | ||
160 | * but prone to "signal hazards", e.g. spurious match appearances that will disappear immediately afterwards. | ||
161 | * | ||
162 | * <p> | ||
163 | * The callback can be unregistered via {@link #removeCallbackOnMatchUpdate(IMatchUpdateListener)}. | ||
164 | * | ||
165 | * @param fireNow | ||
166 | * if true, appearCallback will be immediately invoked on all current matches as a one-time effect. See | ||
167 | * also {@link ViatraQueryMatcher#forEachMatch(IMatchProcessor)}. | ||
168 | * @param listener | ||
169 | * the listener that will be notified of each new match that appears or disappears, starting from now. | ||
170 | * @param matcher | ||
171 | * the {@link ViatraQueryMatcher} for which this listener should be active | ||
172 | */ | ||
173 | public abstract <Match extends IPatternMatch> void addMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
174 | IMatchUpdateListener<? super Match> listener, boolean fireNow); | ||
175 | |||
176 | /** | ||
177 | * Remove an existing match update event listener to this engine instance. | ||
178 | * | ||
179 | * @param matcher | ||
180 | * the {@link ViatraQueryMatcher} for which this listener should not be active anymore | ||
181 | * @param listener | ||
182 | * the {@link IMatchUpdateListener} that should not receive the callbacks anymore | ||
183 | */ | ||
184 | public abstract <Match extends IPatternMatch> void removeMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
185 | IMatchUpdateListener<? super Match> listener); | ||
186 | |||
187 | |||
188 | /** | ||
189 | * Access a pattern matcher based on a {@link IQuerySpecification}, overriding some of the default query evaluation hints. | ||
190 | * Multiple calls may return the same matcher depending on the actual evaluation hints. | ||
191 | * | ||
192 | * <p> It is guaranteed that this method will always return a matcher instance which is functionally compatible | ||
193 | * with the requested functionality (see {@link IMatcherCapability}). | ||
194 | * Otherwise, the query evaluator is free to ignore any hints. | ||
195 | * | ||
196 | * <p> For stateful query backends (Rete), hints may be effective only the first time a matcher is created. | ||
197 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query | ||
198 | * @return a pattern matcher corresponding to the specification | ||
199 | * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with the query | ||
200 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
201 | * @since 0.9 | ||
202 | */ | ||
203 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
204 | IQuerySpecification<Matcher> querySpecification, | ||
205 | QueryEvaluationHint optionalEvaluationHints); | ||
206 | |||
207 | /** | ||
208 | * Initializes matchers for a group of patterns as one step (optionally overriding some of the default query evaluation hints). | ||
209 | * If some of the pattern matchers are already | ||
210 | * constructed in the engine, no task is performed for them. | ||
211 | * | ||
212 | * <p> | ||
213 | * This preparation step has the advantage that it prepares pattern matchers for an arbitrary number of patterns in a | ||
214 | * single-pass traversal of the model. | ||
215 | * This is typically more efficient than traversing the model each time an individual pattern matcher is initialized on demand. | ||
216 | * The performance benefit only manifests itself if the engine is not in wildcard mode. | ||
217 | * | ||
218 | * @param queryGroup a {@link IQueryGroup} identifying a set of VIATRA queries | ||
219 | * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with each query | ||
220 | * @throws ViatraQueryRuntimeException | ||
221 | * if there was an error in preparing the engine | ||
222 | * @since 0.9 | ||
223 | */ | ||
224 | public abstract void prepareGroup(IQueryGroup queryGroup, QueryEvaluationHint optionalEvaluationHints); | ||
225 | |||
226 | /** | ||
227 | * Indicates whether the engine is managed, i.e. the default engine assigned to the given scope root by | ||
228 | * {@link ViatraQueryEngine#on(QueryScope)}. | ||
229 | * | ||
230 | * <p> | ||
231 | * If the engine is managed, there may be other clients using it, as all calls to | ||
232 | * {@link ViatraQueryEngine#on(QueryScope)} return the same managed engine instance for a given scope root. Therefore the | ||
233 | * destructive methods {@link #wipe()} and {@link #dispose()} are not allowed. | ||
234 | * | ||
235 | * <p> | ||
236 | * On the other hand, if the engine is unmanaged (i.e. a private instance created using | ||
237 | * {@link #createUnmanagedEngine(QueryScope)}), then {@link #wipe()} and {@link #dispose()} can be called. If you | ||
238 | * explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
239 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called these | ||
240 | * destructive methods. | ||
241 | * | ||
242 | * @return true if the engine is managed, and therefore potentially shared with other clients querying the same EMF | ||
243 | * model | ||
244 | */ | ||
245 | public abstract boolean isManaged(); | ||
246 | |||
247 | /** | ||
248 | * Indicates whether the engine is in a tainted, inconsistent state due to some internal errors. If true, results | ||
249 | * are no longer reliable; engine should be disposed. | ||
250 | * | ||
251 | * <p> | ||
252 | * The engine is in a tainted state if any of its internal processes report back a fatal error. The | ||
253 | * {@link ViatraQueryEngineLifecycleListener} interface provides a callback method for entering the tainted state. | ||
254 | * | ||
255 | * @return the tainted state | ||
256 | */ | ||
257 | public abstract boolean isTainted(); | ||
258 | |||
259 | /** | ||
260 | * Discards any pattern matcher caches and forgets known patterns. The base index built directly on the underlying | ||
261 | * EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. Use this method if you | ||
262 | * have e.g. new versions of the same patterns, to be matched on the same model. | ||
263 | * | ||
264 | * <p> | ||
265 | * Matcher objects will continue to return stale results. If no references are retained to the matchers, they can | ||
266 | * eventually be GC'ed. | ||
267 | * <p> | ||
268 | * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it. | ||
269 | * <p> | ||
270 | * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
271 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this | ||
272 | * destructive method. | ||
273 | * | ||
274 | * @throws UnsupportedOperationException | ||
275 | * if engine is managed | ||
276 | */ | ||
277 | public abstract void wipe(); | ||
278 | |||
279 | /** | ||
280 | * Completely disconnects and dismantles the engine. Cannot be reversed. | ||
281 | * <p> | ||
282 | * Matcher objects will continue to return stale results. If no references are retained to the matchers or the | ||
283 | * engine, they can eventually be GC'ed, and they won't block the EMF model from being GC'ed anymore. | ||
284 | * <p> | ||
285 | * The base indexer (see {@link #getBaseIndex()}) built on the model will be disposed alongside the engine, unless | ||
286 | * the user has manually added listeners on the base index that were not removed yet. | ||
287 | * <p> | ||
288 | * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it. | ||
289 | * <p> | ||
290 | * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
291 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this | ||
292 | * destructive method. | ||
293 | * | ||
294 | * @throws UnsupportedOperationException | ||
295 | * if engine is managed | ||
296 | */ | ||
297 | public abstract void dispose(); | ||
298 | |||
299 | /** | ||
300 | * Provides access to the selected query backend component of the VIATRA Query engine. | ||
301 | * @noreference for internal use only | ||
302 | * @throws ViatraQueryRuntimeException | ||
303 | */ | ||
304 | public abstract IQueryBackend getQueryBackend(IQueryBackendFactory iQueryBackendFactory); | ||
305 | |||
306 | /** | ||
307 | * Access an existing pattern matcher based on a {@link IQuerySpecification}, and optional hints override. | ||
308 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
309 | * @param optionalOverrideHints a {@link QueryEvaluationHint} that may override the pattern hints (can be null) | ||
310 | * @return a pattern matcher corresponding to the specification, <code>null</code> if a matcher does not exist yet. | ||
311 | * @since 1.4 | ||
312 | */ | ||
313 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher(IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalOverrideHints); | ||
314 | |||
315 | /** | ||
316 | * Returns the immutable {@link ViatraQueryEngineOptions} of the engine. | ||
317 | * | ||
318 | * @return the engine options | ||
319 | * @since 1.4 | ||
320 | */ | ||
321 | public abstract ViatraQueryEngineOptions getEngineOptions(); | ||
322 | |||
323 | /** | ||
324 | * Return the underlying result provider for the given matcher. | ||
325 | * | ||
326 | * @beta This method may change in future versions | ||
327 | * @since 1.4 | ||
328 | * @noreference This method is considered internal API | ||
329 | */ | ||
330 | public abstract IQueryResultProvider getResultProviderOfMatcher(ViatraQueryMatcher<? extends IPatternMatch> matcher); | ||
331 | |||
332 | /** | ||
333 | * The given callable will be executed, and all update propagation in stateful query backends | ||
334 | * will be delayed until the execution is done. Within the callback, these backends will provide stale results. | ||
335 | * | ||
336 | * <p> It is optional for a {@link IQueryBackend} to support the delaying of update propagation; stateless backends will display up-to-date results. | ||
337 | * In this case, the given callable shall be executed, and the update propagation shall happen just like in non-delayed execution. | ||
338 | * | ||
339 | * <p> Example: in the Rete network, no messages will be propagated until the given callable is executed. | ||
340 | * After the execution of the callable, all accumulated messages will be delivered. | ||
341 | * | ||
342 | * <p> The purpose of this method is that stateful query backends may save work when multiple model modifications are performed within the callback that partially cancel each other out. | ||
343 | * | ||
344 | * @param callable the callable to be executed | ||
345 | * @return the result of the callable | ||
346 | * @since 1.6 | ||
347 | */ | ||
348 | public abstract <V> V delayUpdatePropagation(Callable<V> callable) throws InvocationTargetException; | ||
349 | |||
350 | /** | ||
351 | * Returns true if the update propagation in this engine is currently delayed, false otherwise. | ||
352 | * | ||
353 | * @see {@link #delayUpdatePropagation(Callable)} | ||
354 | * @since 1.6 | ||
355 | */ | ||
356 | public abstract boolean isUpdatePropagationDelayed(); | ||
357 | |||
358 | /** | ||
359 | * Returns true if the {@link #dispose()} method was called on this engine previously. | ||
360 | * @since 2.0 | ||
361 | */ | ||
362 | public abstract boolean isDisposed(); | ||
363 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatch.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatch.java new file mode 100644 index 00000000..b4de2b70 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatch.java | |||
@@ -0,0 +1,166 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.api.impl.BasePatternMatch; | ||
15 | |||
16 | /** | ||
17 | * Generic signature object implementation. | ||
18 | * | ||
19 | * See also the generated matcher and signature of the pattern, with pattern-specific API simplifications. | ||
20 | * | ||
21 | * @author Bergmann Gábor | ||
22 | * @since 0.9 | ||
23 | * | ||
24 | */ | ||
25 | public abstract class GenericPatternMatch extends BasePatternMatch { | ||
26 | |||
27 | private final GenericQuerySpecification<? extends GenericPatternMatcher> specification; | ||
28 | private final Object[] array; | ||
29 | |||
30 | private GenericPatternMatch(GenericQuerySpecification<? extends GenericPatternMatcher> specification, Object[] array) { | ||
31 | super(); | ||
32 | this.specification = specification; | ||
33 | this.array = array; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Object get(String parameterName) { | ||
38 | Integer index = specification.getPositionOfParameter(parameterName); | ||
39 | return index == null ? null : array[index]; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public Object get(int position) { | ||
44 | return array[position]; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public boolean set(String parameterName, Object newValue) { | ||
49 | if (!isMutable()) throw new UnsupportedOperationException(); | ||
50 | Integer index = specification.getPositionOfParameter(parameterName); | ||
51 | if (index == null) | ||
52 | return false; | ||
53 | array[index] = newValue; | ||
54 | return true; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public Object[] toArray() { | ||
59 | return Arrays.copyOf(array, array.length); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public int hashCode() { | ||
64 | final int prime = 31; | ||
65 | int result = 1; | ||
66 | for (int i = 0; i < array.length; ++i) | ||
67 | result = prime * result + ((array[i] == null) ? 0 : array[i].hashCode()); | ||
68 | return result; | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public boolean equals(Object obj) { | ||
73 | if (this == obj) | ||
74 | return true; | ||
75 | if (!(obj instanceof GenericPatternMatch)) { // this should be infrequent | ||
76 | if (obj == null) | ||
77 | return false; | ||
78 | if (!(obj instanceof IPatternMatch)) | ||
79 | return false; | ||
80 | IPatternMatch other = (IPatternMatch) obj; | ||
81 | if (!specification().equals(other.specification())) | ||
82 | return false; | ||
83 | return Arrays.deepEquals(array, other.toArray()); | ||
84 | } | ||
85 | final GenericPatternMatch other = (GenericPatternMatch) obj; | ||
86 | return specification().equals(other.specification()) && Arrays.deepEquals(array, other.array); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public String prettyPrint() { | ||
91 | StringBuilder result = new StringBuilder(); | ||
92 | for (int i = 0; i < array.length; ++i) { | ||
93 | if (i != 0) | ||
94 | result.append(", "); | ||
95 | result.append("\"" + parameterNames().get(i) + "\"=" + prettyPrintValue(array[i])); | ||
96 | } | ||
97 | return result.toString(); | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public GenericQuerySpecification<? extends GenericPatternMatcher> specification() { | ||
102 | return specification; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Returns an empty, mutable match. | ||
107 | * Fields of the mutable match can be filled to create a partial match, usable as matcher input. | ||
108 | * | ||
109 | * @return the empty match | ||
110 | */ | ||
111 | public static GenericPatternMatch newEmptyMatch(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { | ||
112 | return new Mutable(specification, new Object[specification.getParameters().size()]); | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Returns a mutable (partial) match. | ||
117 | * Fields of the mutable match can be filled to create a partial match, usable as matcher input. | ||
118 | * | ||
119 | * @param parameters | ||
120 | * the fixed value of pattern parameters, or null if not bound. | ||
121 | * @return the new, mutable (partial) match object. | ||
122 | */ | ||
123 | public static GenericPatternMatch newMutableMatch(GenericQuerySpecification<? extends GenericPatternMatcher> specification, Object... parameters) { | ||
124 | return new Mutable(specification, parameters); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * Returns a new (partial) match. | ||
129 | * This can be used e.g. to call the matcher with a partial match. | ||
130 | * | ||
131 | * <p>The returned match will be immutable. Use {@link #newEmptyMatch(GenericQuerySpecification)} to obtain a mutable match object. | ||
132 | * | ||
133 | * @param parameters | ||
134 | * the fixed value of pattern parameters, or null if not bound. | ||
135 | * @return the (partial) match object. | ||
136 | */ | ||
137 | public static GenericPatternMatch newMatch(GenericQuerySpecification<? extends GenericPatternMatcher> specification, Object... parameters) { | ||
138 | return new Immutable(specification, Arrays.copyOf(parameters, parameters.length)); | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public IPatternMatch toImmutable() { | ||
143 | return isMutable() ? newMatch(specification, array) : this; | ||
144 | } | ||
145 | |||
146 | static final class Mutable extends GenericPatternMatch { | ||
147 | Mutable(GenericQuerySpecification<? extends GenericPatternMatcher> specification, Object[] array) { | ||
148 | super(specification, array); | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public boolean isMutable() { | ||
153 | return true; | ||
154 | } | ||
155 | } | ||
156 | static final class Immutable extends GenericPatternMatch { | ||
157 | Immutable(GenericQuerySpecification<? extends GenericPatternMatcher> specification, Object[] array) { | ||
158 | super(specification, array); | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public boolean isMutable() { | ||
163 | return false; | ||
164 | } | ||
165 | } | ||
166 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatcher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatcher.java new file mode 100644 index 00000000..9a3fbb44 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatcher.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.api.impl.BaseMatcher; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | |||
15 | /** | ||
16 | * This is a generic pattern matcher for any VIATRA pattern, with "interpretative" query execution. | ||
17 | * To use the pattern matcher on a given model, obtain a {@link GenericQuerySpecification} first, then | ||
18 | * invoke e.g. {@link GenericQuerySpecification#getMatcher(ViatraQueryEngine)}. | ||
19 | * in conjunction with {@link ViatraQueryEngine#on(tools.refinery.viatra.runtime.api.scope.QueryScope)}. | ||
20 | * <p> | ||
21 | * Whenever available, consider using the pattern-specific generated matcher API instead. | ||
22 | * | ||
23 | * <p> | ||
24 | * Matches of the pattern will be represented as {@link GenericPatternMatch}. | ||
25 | * | ||
26 | * @author Bergmann Gábor | ||
27 | * @see GenericPatternMatch | ||
28 | * @see GenericMatchProcessor | ||
29 | * @see GenericQuerySpecification | ||
30 | * @since 0.9 | ||
31 | */ | ||
32 | public class GenericPatternMatcher extends BaseMatcher<GenericPatternMatch> { | ||
33 | |||
34 | /** | ||
35 | * @since 1.4 | ||
36 | */ | ||
37 | public GenericPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { | ||
38 | super(specification); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public GenericPatternMatch arrayToMatch(Object[] parameters) { | ||
43 | return GenericPatternMatch.newMatch(getSpecification(), parameters); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public GenericPatternMatch arrayToMatchMutable(Object[] parameters) { | ||
48 | return GenericPatternMatch.newMutableMatch(getSpecification(), parameters); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | protected GenericPatternMatch tupleToMatch(Tuple t) { | ||
53 | return new GenericPatternMatch.Immutable(getSpecification(), /*avoid re-cloning*/t.getElements()); | ||
54 | } | ||
55 | |||
56 | @SuppressWarnings("unchecked") | ||
57 | @Override | ||
58 | public GenericQuerySpecification<? extends GenericPatternMatcher> getSpecification() { | ||
59 | return (GenericQuerySpecification<? extends GenericPatternMatcher>)querySpecification; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Internal method for {@link GenericQuerySpecification} | ||
64 | * @noreference | ||
65 | */ | ||
66 | static <Matcher extends GenericPatternMatcher> GenericPatternMatcher instantiate(GenericQuerySpecification<Matcher> querySpecification) { | ||
67 | return new GenericPatternMatcher(querySpecification); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Internal method for {@link GenericQuerySpecification} | ||
72 | * @noreference | ||
73 | */ | ||
74 | static <Matcher extends GenericPatternMatcher> GenericPatternMatcher instantiate(ViatraQueryEngine engine, GenericQuerySpecification<Matcher> querySpecification) { | ||
75 | // check if matcher already exists | ||
76 | GenericPatternMatcher matcher = engine.getExistingMatcher(querySpecification); | ||
77 | if (matcher == null) { | ||
78 | matcher = engine.getMatcher(querySpecification); | ||
79 | } | ||
80 | return matcher; | ||
81 | } | ||
82 | |||
83 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQueryGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQueryGroup.java new file mode 100644 index 00000000..a5661bc9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQueryGroup.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Mark Czotter, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.api.impl.BaseQueryGroup; | ||
18 | |||
19 | /** | ||
20 | * Generic implementation of {@link IQueryGroup}, covering an arbitrarily chosen set of patterns. Use the public | ||
21 | * constructor or static GenericQueryGroup.of(...) methods to instantiate. | ||
22 | * | ||
23 | * @author Mark Czotter | ||
24 | * | ||
25 | */ | ||
26 | public class GenericQueryGroup extends BaseQueryGroup { | ||
27 | |||
28 | private final Set<IQuerySpecification<?>> patterns; | ||
29 | |||
30 | /** | ||
31 | * Creates a GenericQueryGroup object with a set of patterns. | ||
32 | * | ||
33 | * @param patterns | ||
34 | */ | ||
35 | public GenericQueryGroup(Set<IQuerySpecification<?>> patterns) { | ||
36 | this.patterns = patterns; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Set<IQuerySpecification<?>> getSpecifications() { | ||
41 | return patterns; | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Creates a generic {@link IQueryGroup} instance from {@link IQuerySpecification} objects. | ||
46 | * | ||
47 | * @since 2.0 | ||
48 | */ | ||
49 | public static IQueryGroup of(Stream<IQuerySpecification<?>> querySpecifications) { | ||
50 | return new GenericQueryGroup(querySpecifications.collect(Collectors.toSet())); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * Creates a generic {@link IQueryGroup} instance from {@link IQuerySpecification} objects. | ||
55 | * | ||
56 | * @param querySpecifications | ||
57 | */ | ||
58 | public static IQueryGroup of(Set<IQuerySpecification<?>> querySpecifications) { | ||
59 | return new GenericQueryGroup(querySpecifications); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Creates a generic {@link IQueryGroup} instance from {@link IQuerySpecification} objects. | ||
64 | * | ||
65 | * @param querySpecifications | ||
66 | */ | ||
67 | public static IQueryGroup of(IQuerySpecification<?>... querySpecifications) { | ||
68 | return of(new HashSet<IQuerySpecification<?>>(Arrays.asList(querySpecifications))); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Creates a generic {@link IQueryGroup} instance from other {@link IQueryGroup} objects (subgroups). | ||
73 | * | ||
74 | */ | ||
75 | public static IQueryGroup of(IQueryGroup... subGroups) { | ||
76 | Set<IQuerySpecification<?>> patterns = new HashSet<IQuerySpecification<?>>(); | ||
77 | for (IQueryGroup group : subGroups) { | ||
78 | patterns.addAll(group.getSpecifications()); | ||
79 | } | ||
80 | return new GenericQueryGroup(patterns); | ||
81 | } | ||
82 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQuerySpecification.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQuerySpecification.java new file mode 100644 index 00000000..5681ac19 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQuerySpecification.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.impl.BaseQuerySpecification; | ||
12 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility; | ||
15 | |||
16 | /** | ||
17 | * This is a generic query specification for VIATRA pattern matchers, for "interpretative" query execution. | ||
18 | * Should be subclassed by query specification implementations specific to query languages. | ||
19 | * | ||
20 | * <p> | ||
21 | * When available, consider using the pattern-specific generated matcher API instead. | ||
22 | * | ||
23 | * <p> | ||
24 | * The created matcher will be of type {@link GenericPatternMatcher}. Matches of the pattern will be represented as | ||
25 | * {@link GenericPatternMatch}. | ||
26 | * | ||
27 | * <p> | ||
28 | * Note for overriding (if you have your own query language or ): | ||
29 | * Derived classes should use {@link #defaultInstantiate(ViatraQueryEngine)} for implementing | ||
30 | * {@link #instantiate(ViatraQueryEngine)} if they use {@link GenericPatternMatcher} proper. | ||
31 | * | ||
32 | * @see GenericPatternMatcher | ||
33 | * @see GenericPatternMatch | ||
34 | * @see GenericMatchProcessor | ||
35 | * @author Bergmann Gábor | ||
36 | * @noinstantiate This class is not intended to be instantiated by end-users. | ||
37 | * @since 0.9 | ||
38 | */ | ||
39 | public abstract class GenericQuerySpecification<Matcher extends GenericPatternMatcher> extends | ||
40 | BaseQuerySpecification<Matcher> { | ||
41 | |||
42 | /** | ||
43 | * Instantiates query specification for the given internal query representation. | ||
44 | */ | ||
45 | public GenericQuerySpecification(PQuery wrappedPQuery) { | ||
46 | super(wrappedPQuery); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public GenericPatternMatch newEmptyMatch() { | ||
51 | return GenericPatternMatch.newEmptyMatch(this); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public GenericPatternMatch newMatch(Object... parameters) { | ||
56 | return GenericPatternMatch.newMatch(this, parameters); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Derived classes should use this implementation of {@link #instantiate(ViatraQueryEngine)} | ||
61 | * if they use {@link GenericPatternMatcher} proper. | ||
62 | * @throws ViatraQueryRuntimeException | ||
63 | */ | ||
64 | protected GenericPatternMatcher defaultInstantiate(ViatraQueryEngine engine) { | ||
65 | return GenericPatternMatcher.instantiate(engine, this); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * @since 2.0 | ||
70 | */ | ||
71 | @Override | ||
72 | public PVisibility getVisibility() { | ||
73 | return getInternalQueryRepresentation().getVisibility(); | ||
74 | } | ||
75 | |||
76 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IMatchUpdateListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IMatchUpdateListener.java new file mode 100644 index 00000000..23c64537 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IMatchUpdateListener.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | /** | ||
12 | * An interface for low-level notifications about match appearance and disappearance. | ||
13 | * | ||
14 | * <p> | ||
15 | * See {@link ViatraQueryMatcher#addCallbackOnMatchUpdate(IMatchUpdateListener, boolean)} for usage. Clients should | ||
16 | * consider using {@link MatchUpdateAdapter} or deriving their implementation from it. | ||
17 | * | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public interface IMatchUpdateListener<Match extends IPatternMatch> { | ||
22 | /** | ||
23 | * Will be invoked on each new match that appears. | ||
24 | * | ||
25 | * @param match | ||
26 | * the match that has just appeared. | ||
27 | */ | ||
28 | public void notifyAppearance(Match match); | ||
29 | |||
30 | /** | ||
31 | * Will be invoked on each existing match that disappears. | ||
32 | * | ||
33 | * @param match | ||
34 | * the match that has just disappeared. | ||
35 | */ | ||
36 | public void notifyDisappearance(Match match); | ||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IPatternMatch.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IPatternMatch.java new file mode 100644 index 00000000..be6467cc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IPatternMatch.java | |||
@@ -0,0 +1,107 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import java.util.List; | ||
13 | |||
14 | /** | ||
15 | * Generic interface for a single match of a pattern. Each instance is a (partial) substitution of pattern parameters, | ||
16 | * essentially a parameter to value mapping. | ||
17 | * | ||
18 | * <p>Can also represent a partial match; unsubstituted parameters are assigned to null. Pattern matchers must never return | ||
19 | * a partial match, but they accept partial matches as method parameters. | ||
20 | * | ||
21 | * @author Bergmann Gábor | ||
22 | */ | ||
23 | public interface IPatternMatch extends Cloneable /* , Map<String, Object> */{ | ||
24 | /** @return the pattern for which this is a match. */ | ||
25 | public IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> specification(); | ||
26 | |||
27 | /** Identifies the name of the pattern for which this is a match. */ | ||
28 | public String patternName(); | ||
29 | |||
30 | /** Returns the list of symbolic parameter names. */ | ||
31 | public List<String> parameterNames(); | ||
32 | |||
33 | /** Returns the value of the parameter with the given name, or null if name is invalid. */ | ||
34 | public Object get(String parameterName); | ||
35 | |||
36 | /** Returns the value of the parameter at the given position, or null if position is invalid. */ | ||
37 | public Object get(int position); | ||
38 | |||
39 | /** | ||
40 | * Sets the parameter with the given name to the given value. | ||
41 | * | ||
42 | * <p> Works only if match is mutable. See {@link #isMutable()}. | ||
43 | * | ||
44 | * @returns true if successful, false if parameter name is invalid. May also fail and return false if the value type | ||
45 | * is incompatible. | ||
46 | * @throws UnsupportedOperationException if match is not mutable. | ||
47 | */ | ||
48 | public boolean set(String parameterName, Object newValue); | ||
49 | |||
50 | /** | ||
51 | * Sets the parameter at the given position to the given value. | ||
52 | * | ||
53 | * <p> Works only if match is mutable. See {@link #isMutable()}. | ||
54 | * | ||
55 | * @returns true if successful, false if position is invalid. May also fail and return false if the value type is | ||
56 | * incompatible. | ||
57 | * @throws UnsupportedOperationException if match is not mutable. | ||
58 | */ | ||
59 | public boolean set(int position, Object newValue); | ||
60 | |||
61 | /** | ||
62 | * Returns whether the match object can be further modified after its creation. Setters work only if the match is mutable. | ||
63 | * | ||
64 | * <p>Matches computed by the pattern matchers are not mutable, so that the match set cannot be modified externally. | ||
65 | * Partial matches used as matcher input, however, can be mutable; such match objects can be created using {@link ViatraQueryMatcher#newEmptyMatch()}. | ||
66 | * | ||
67 | * @return whether the match can be modified | ||
68 | */ | ||
69 | public boolean isMutable(); | ||
70 | |||
71 | /** | ||
72 | * Converts the match to an array representation, with each pattern parameter at their respective position. | ||
73 | * In case of a partial match, unsubstituted parameters will be represented as null elements in the array. | ||
74 | * | ||
75 | * @return a newly constructed array containing each parameter substitution of the match in order. | ||
76 | */ | ||
77 | public Object[] toArray(); | ||
78 | |||
79 | /** | ||
80 | * Takes an immutable snapshot of this match. | ||
81 | * @return the match itself in case of immutable matches, an immutable copy in case of mutable ones. | ||
82 | */ | ||
83 | public IPatternMatch toImmutable(); | ||
84 | |||
85 | /** Prints the list of parameter-value pairs. */ | ||
86 | public String prettyPrint(); | ||
87 | |||
88 | /** | ||
89 | * Checks that this match is compatible with the given other match. | ||
90 | * This is used for filtering the match set of a matcher. | ||
91 | * | ||
92 | * <p/> Two non-null matches are compatible if and only if: | ||
93 | * <ul> | ||
94 | * <li>They share the same pattern.</li> | ||
95 | * <li>For each parameter, where they are set (non-null) in both matches, | ||
96 | * their values are equal.</li> | ||
97 | * </li> | ||
98 | * </ul> | ||
99 | * | ||
100 | * <p/> Furthermore, all matches are considered compatible with | ||
101 | * null matches (e.g. empty filter). | ||
102 | * | ||
103 | * @param other | ||
104 | * @return true, if this is compatible with other, or other is null | ||
105 | */ | ||
106 | public boolean isCompatibleWith(IPatternMatch other); | ||
107 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQueryGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQueryGroup.java new file mode 100644 index 00000000..a783f823 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQueryGroup.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Mark Czotter, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | /** | ||
14 | * Generic interface for group of query specifications. | ||
15 | * | ||
16 | * <p>It handles more than one patterns as a group, and provides functionality to initialize the matchers together (which | ||
17 | * has performance benefits). | ||
18 | * | ||
19 | * @author Mark Czotter | ||
20 | * | ||
21 | */ | ||
22 | public interface IQueryGroup { | ||
23 | |||
24 | /** | ||
25 | * Initializes matchers for the group of patterns within an {@link ViatraQueryEngine}. If some of the pattern matchers are already | ||
26 | * constructed in the engine, no task is performed for them. | ||
27 | * | ||
28 | * <p> | ||
29 | * This preparation step has the advantage that it prepares pattern matchers for an arbitrary number of patterns in a | ||
30 | * single-pass traversal of the model. | ||
31 | * This is typically more efficient than traversing the model each time an individual pattern matcher is initialized on demand. | ||
32 | * The performance benefit only manifests itself if the engine is not in wildcard mode. | ||
33 | * | ||
34 | * @param engine | ||
35 | * the existing VIATRA Query engine in which the matchers will be created. | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | * if there was an error in preparing the engine | ||
38 | */ | ||
39 | public void prepare(ViatraQueryEngine engine); | ||
40 | |||
41 | /** | ||
42 | * Returns the currently assigned {@link IQuerySpecification}s. | ||
43 | */ | ||
44 | public Set<IQuerySpecification<?>> getSpecifications(); | ||
45 | |||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQuerySpecification.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQuerySpecification.java new file mode 100644 index 00000000..326d2202 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQuerySpecification.java | |||
@@ -0,0 +1,86 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueryHeader; | ||
16 | |||
17 | /** | ||
18 | * API interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher | ||
19 | * of the pattern with various parameters. | ||
20 | * | ||
21 | * <p> As of 0.9.0, some internal details (mostly relevant for query evaluator backends) have been moved to {@link #getInternalQueryRepresentation()}. | ||
22 | * | ||
23 | * @author Bergmann Gábor | ||
24 | * | ||
25 | */ | ||
26 | public interface IQuerySpecification<Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> extends PQueryHeader { | ||
27 | |||
28 | /** | ||
29 | * Initializes the pattern matcher within an existing {@link ViatraQueryEngine}. If the pattern matcher is already | ||
30 | * constructed in the engine, only a lightweight reference is created. | ||
31 | * <p> | ||
32 | * The match set will be incrementally refreshed upon updates. | ||
33 | * | ||
34 | * @param engine | ||
35 | * the existing VIATRA Query engine in which this matcher will be created. | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | * if an error occurs during pattern matcher creation | ||
38 | */ | ||
39 | public Matcher getMatcher(ViatraQueryEngine engine); | ||
40 | |||
41 | |||
42 | /** | ||
43 | * Returns an empty, mutable Match compatible with matchers of this query. | ||
44 | * Fields of the mutable match can be filled to create a partial match, usable as matcher input. | ||
45 | * This can be used to call the matcher with a partial match | ||
46 | * even if the specific class of the matcher or the match is unknown. | ||
47 | * | ||
48 | * @return the empty match | ||
49 | */ | ||
50 | public abstract IPatternMatch newEmptyMatch(); | ||
51 | |||
52 | /** | ||
53 | * Returns a new (partial) Match object compatible with matchers of this query. | ||
54 | * This can be used e.g. to call the matcher with a partial | ||
55 | * match. | ||
56 | * | ||
57 | * <p>The returned match will be immutable. Use {@link #newEmptyMatch()} to obtain a mutable match object. | ||
58 | * | ||
59 | * @param parameters | ||
60 | * the fixed value of pattern parameters, or null if not bound. | ||
61 | * @return the (partial) match object. | ||
62 | */ | ||
63 | public abstract IPatternMatch newMatch(Object... parameters); | ||
64 | |||
65 | /** | ||
66 | * The query is formulated over this kind of modeling platform. | ||
67 | * E.g. for queries over EMF models, the {@link EMFScope} class is returned. | ||
68 | */ | ||
69 | public Class<? extends QueryScope> getPreferredScopeClass(); | ||
70 | |||
71 | /** | ||
72 | * Returns the definition of the query in a format intended for consumption by the query evaluator. | ||
73 | * @return the internal representation of the query. | ||
74 | */ | ||
75 | public PQuery getInternalQueryRepresentation(); | ||
76 | |||
77 | /** | ||
78 | * Creates a new uninitialized matcher, which is not functional until an engine initializes it. Clients | ||
79 | * should not call this method, it is used by the {@link ViatraQueryEngine} instance to instantiate matchers. | ||
80 | * @throws ViatraQueryRuntimeException | ||
81 | * @noreference This method is not intended to be referenced by clients. | ||
82 | * @since 1.4 | ||
83 | */ | ||
84 | public Matcher instantiate(); | ||
85 | |||
86 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/MatchUpdateAdapter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/MatchUpdateAdapter.java new file mode 100644 index 00000000..7de6d2c6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/MatchUpdateAdapter.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | import java.util.function.Consumer; | ||
12 | |||
13 | /** | ||
14 | * A default implementation of {@link IMatchUpdateListener} that contains two match processors, one for appearance, one | ||
15 | * for disappearance. Any of the two can be null; in this case, corresponding notifications will be ignored. | ||
16 | * | ||
17 | * <p> | ||
18 | * Instantiate using either constructor. | ||
19 | * | ||
20 | * @author Bergmann Gabor | ||
21 | * | ||
22 | */ | ||
23 | public class MatchUpdateAdapter<Match extends IPatternMatch> implements IMatchUpdateListener<Match> { | ||
24 | |||
25 | Consumer<Match> appearCallback; | ||
26 | Consumer<Match> disappearCallback; | ||
27 | |||
28 | /** | ||
29 | * Constructs an instance without any match processors registered yet. | ||
30 | * | ||
31 | * Use {@link #setAppearCallback(Consumer)} and {@link #setDisappearCallback(Consumer)} to specify | ||
32 | * optional match processors for match appearance and disappearance, respectively. | ||
33 | */ | ||
34 | public MatchUpdateAdapter() { | ||
35 | super(); | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Constructs an instance by specifying match processors. | ||
40 | * | ||
41 | * @param appearCallback | ||
42 | * a match processor that will be invoked on each new match that appears. If null, no callback will be | ||
43 | * executed on match appearance. See {@link Consumer} for details on how to implement. | ||
44 | * @param disappearCallback | ||
45 | * a match processor that will be invoked on each existing match that disappears. If null, no callback | ||
46 | * will be executed on match disappearance. See {@link Consumer} for details on how to implement. | ||
47 | * @since 2.0 | ||
48 | */ | ||
49 | public MatchUpdateAdapter(Consumer<Match> appearCallback, Consumer<Match> disappearCallback) { | ||
50 | super(); | ||
51 | setAppearCallback(appearCallback); | ||
52 | setDisappearCallback(disappearCallback); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @return the match processor that will be invoked on each new match that appears. If null, no callback will be | ||
57 | * executed on match appearance. | ||
58 | * @since 2.0 | ||
59 | */ | ||
60 | public Consumer<Match> getAppearCallback() { | ||
61 | return appearCallback; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * @param appearCallback | ||
66 | * a match processor that will be invoked on each new match that appears. If null, no callback will be | ||
67 | * executed on match appearance. See {@link Consumer} for details on how to implement. | ||
68 | * @since 2.0 | ||
69 | */ | ||
70 | public void setAppearCallback(Consumer<Match> appearCallback) { | ||
71 | this.appearCallback = appearCallback; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @return the match processor that will be invoked on each existing match that disappears. If null, no callback | ||
76 | * will be executed on match disappearance. | ||
77 | * @since 2.0 | ||
78 | */ | ||
79 | public Consumer<Match> getDisappearCallback() { | ||
80 | return disappearCallback; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * @param disappearCallback | ||
85 | * a match processor that will be invoked on each existing match that disappears. If null, no callback | ||
86 | * will be executed on match disappearance. See {@link Consumer} for details on how to implement. | ||
87 | * @since 2.0 | ||
88 | */ | ||
89 | public void setDisappearCallback(Consumer<Match> disappearCallback) { | ||
90 | this.disappearCallback = disappearCallback; | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public void notifyAppearance(Match match) { | ||
95 | if (appearCallback != null) | ||
96 | appearCallback.accept(match); | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public void notifyDisappearance(Match match) { | ||
101 | if (disappearCallback != null) | ||
102 | disappearCallback.accept(match); | ||
103 | } | ||
104 | |||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java new file mode 100644 index 00000000..0f402b49 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java | |||
@@ -0,0 +1,153 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | |||
11 | package tools.refinery.viatra.runtime.api; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
15 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
16 | |||
17 | import java.util.Set; | ||
18 | import java.util.function.Supplier; | ||
19 | import java.util.stream.Collectors; | ||
20 | |||
21 | /** | ||
22 | * A Viatra Query (incremental) evaluation engine, attached to a model such as an EMF resource. The engine hosts pattern matchers, and | ||
23 | * will listen on model update notifications stemming from the given model in order to maintain live results. | ||
24 | * | ||
25 | * <p> | ||
26 | * By default, ViatraQueryEngines do not need to be separately disposed; they will be garbage collected along with the model. | ||
27 | * Advanced users: see {@link AdvancedViatraQueryEngine} if you want fine control over the lifecycle of an engine. | ||
28 | * | ||
29 | * <p> | ||
30 | * Pattern matchers within this engine may be instantiated in the following ways: | ||
31 | * <ul> | ||
32 | * <li>Recommended: instantiate the specific matcher class generated for the pattern by e.g. MyPatternMatcher.on(engine). | ||
33 | * <li>Use {@link #getMatcher(IQuerySpecification)} if the pattern-specific generated matcher API is not available. | ||
34 | * <li>Advanced: use the query specification associated with the generated matcher class to achieve the same. | ||
35 | * </ul> | ||
36 | * Additionally, a group of patterns (see {@link IQueryGroup}) can be initialized together before usage; this may improve | ||
37 | * the performance of pattern matcher construction by trying to gather all necessary information from the model in one go. | ||
38 | * Note that no such improvement is to be expected if the engine is specifically constructed in wildcard mode, | ||
39 | * an option available in some scope implementations | ||
40 | * (see {@link EMFScope#EMFScope(Notifier, BaseIndexOptions)} and {@link BaseIndexOptions#withWildcardMode(boolean)}). | ||
41 | * | ||
42 | * | ||
43 | * @author Bergmann Gábor | ||
44 | * @noextend This class is not intended to be subclassed by clients. | ||
45 | */ | ||
46 | public abstract class ViatraQueryEngine { | ||
47 | |||
48 | |||
49 | /** | ||
50 | * Obtain a (managed) {@link ViatraQueryEngine} to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
51 | * | ||
52 | * <p> For a given matcher scope, the same engine will be returned to any client. | ||
53 | * This facilitates the reuse of internal caches of the engine, greatly improving performance. | ||
54 | * | ||
55 | * <p> The lifecycle of this engine is centrally managed, and will not be disposed as long as the model is retained in memory. | ||
56 | * The engine will be garbage collected along with the model. | ||
57 | * | ||
58 | * <p> | ||
59 | * Advanced users: see {@link AdvancedViatraQueryEngine#createUnmanagedEngine(QueryScope)} to obtain a private, | ||
60 | * unmanaged engine that is not shared with other clients and allows tight control over its lifecycle. | ||
61 | * | ||
62 | * @param scope | ||
63 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
64 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
65 | * @return a (managed) {@link ViatraQueryEngine} instance | ||
66 | */ | ||
67 | public static ViatraQueryEngine on(QueryScope scope) { | ||
68 | return ViatraQueryEngineManager.getInstance().getQueryEngine(scope); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Obtain a (managed) {@link ViatraQueryEngine} to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
73 | * | ||
74 | * <p> For a given matcher scope, the same engine will be returned to any client. | ||
75 | * This facilitates the reuse of internal caches of the engine, greatly improving performance. | ||
76 | * | ||
77 | * <p> The lifecycle of this engine is centrally managed, and will not be disposed as long as the model is retained in memory. | ||
78 | * The engine will be garbage collected along with the model. | ||
79 | * | ||
80 | * <p> | ||
81 | * Advanced users: see {@link AdvancedViatraQueryEngine#createUnmanagedEngine(QueryScope)} to obtain a private, | ||
82 | * unmanaged engine that is not shared with other clients and allows tight control over its lifecycle. | ||
83 | * | ||
84 | * @param scope | ||
85 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
86 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
87 | * @return a (managed) {@link ViatraQueryEngine} instance | ||
88 | * @since 1.4 | ||
89 | */ | ||
90 | public static ViatraQueryEngine on(QueryScope scope, ViatraQueryEngineOptions options) { | ||
91 | return ViatraQueryEngineManager.getInstance().getQueryEngine(scope, options); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Provides access to the internal base index component of the engine, responsible for keeping track of basic | ||
96 | * contents of the model. | ||
97 | * | ||
98 | * <p>If using an {@link EMFScope}, | ||
99 | * consider {@link EMFScope#extractUnderlyingEMFIndex(ViatraQueryEngine)} instead to access EMF-specific details. | ||
100 | * | ||
101 | * @return the baseIndex the NavigationHelper maintaining the base index | ||
102 | * @throws ViatraQueryRuntimeException | ||
103 | * if the base index could not be constructed | ||
104 | */ | ||
105 | public abstract IBaseIndex getBaseIndex(); | ||
106 | |||
107 | /** | ||
108 | * Access a pattern matcher based on a {@link IQuerySpecification}. | ||
109 | * Multiple calls will return the same matcher. | ||
110 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
111 | * @return a pattern matcher corresponding to the specification | ||
112 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
113 | */ | ||
114 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher(IQuerySpecification<Matcher> querySpecification); | ||
115 | |||
116 | /** | ||
117 | * Access a pattern matcher for the graph pattern with the given fully qualified name. | ||
118 | * Will succeed only if a query specification for this fully qualified name has been generated and registered. | ||
119 | * Multiple calls will return the same matcher unless the registered specification changes. | ||
120 | * | ||
121 | * @param patternFQN the fully qualified name of a VIATRA query specification | ||
122 | * @return a pattern matcher corresponding to the specification | ||
123 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
124 | */ | ||
125 | public abstract ViatraQueryMatcher<? extends IPatternMatch> getMatcher(String patternFQN); | ||
126 | |||
127 | /** | ||
128 | * Access an existing pattern matcher based on a {@link IQuerySpecification}. | ||
129 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
130 | * @return a pattern matcher corresponding to the specification, <code>null</code> if a matcher does not exist yet. | ||
131 | */ | ||
132 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher(IQuerySpecification<Matcher> querySpecification); | ||
133 | |||
134 | |||
135 | /** | ||
136 | * Access a copy of available {@link ViatraQueryMatcher} pattern matchers. | ||
137 | * @return a copy of the set of currently available pattern matchers registered on this engine instance | ||
138 | */ | ||
139 | public abstract Set<? extends ViatraQueryMatcher<? extends IPatternMatch>> getCurrentMatchers(); | ||
140 | |||
141 | public Set<IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>>> getRegisteredQuerySpecifications() { | ||
142 | return getCurrentMatchers().stream().map(ViatraQueryMatcher::getSpecification).collect(Collectors.toSet()); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * @return the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
147 | */ | ||
148 | public abstract QueryScope getScope(); | ||
149 | |||
150 | public abstract void flushChanges(); | ||
151 | |||
152 | public abstract <T> T withFlushingChanges(Supplier<T> supplier); | ||
153 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineInitializationListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineInitializationListener.java new file mode 100644 index 00000000..02162a65 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineInitializationListener.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | /** | ||
12 | * Listener interface to get notifications when a new managed engine is initialized. | ||
13 | * | ||
14 | * @author Abel Hegedus | ||
15 | * | ||
16 | */ | ||
17 | public interface ViatraQueryEngineInitializationListener { | ||
18 | |||
19 | /** | ||
20 | * Called when a managed engine is initialized in the EngineManager. | ||
21 | * | ||
22 | * @param engine the initialized engine | ||
23 | */ | ||
24 | void engineInitialized(AdvancedViatraQueryEngine engine); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineLifecycleListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineLifecycleListener.java new file mode 100644 index 00000000..1c4dc264 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineLifecycleListener.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | |||
12 | /** | ||
13 | * Listener interface for getting notification on changes in an {@link ViatraQueryEngine}. | ||
14 | * | ||
15 | * You can use it to remove any other listeners that you attached to matchers or the engine, | ||
16 | * or to handle matchers that are initialized after you started using the engine. | ||
17 | * | ||
18 | * @author Abel Hegedus | ||
19 | * | ||
20 | */ | ||
21 | public interface ViatraQueryEngineLifecycleListener { | ||
22 | |||
23 | // ------------------------------------------------------------------------------- | ||
24 | // MATCHERS (methods notifying on changes in the matchers available in the engine) | ||
25 | // ------------------------------------------------------------------------------- | ||
26 | |||
27 | /** | ||
28 | * Called after a matcher is instantiated in the engine | ||
29 | * | ||
30 | * @param matcher the new matcher | ||
31 | */ | ||
32 | void matcherInstantiated(ViatraQueryMatcher<? extends IPatternMatch> matcher); | ||
33 | |||
34 | // ------------------------------------------------------------------------- | ||
35 | // HEALTH (methods notifying on changes that affect the health of the engine | ||
36 | // ------------------------------------------------------------------------- | ||
37 | |||
38 | /** | ||
39 | * Called after the engine has become tainted due to a fatal error | ||
40 | */ | ||
41 | void engineBecameTainted(String message, Throwable t); | ||
42 | |||
43 | /** | ||
44 | * Called after the engine has been wiped | ||
45 | */ | ||
46 | void engineWiped(); | ||
47 | |||
48 | /** | ||
49 | * Called after the engine has been disposed | ||
50 | */ | ||
51 | void engineDisposed(); | ||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineManager.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineManager.java new file mode 100644 index 00000000..4a256aea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineManager.java | |||
@@ -0,0 +1,191 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
13 | import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl; | ||
14 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
15 | |||
16 | import java.lang.ref.WeakReference; | ||
17 | import java.util.*; | ||
18 | |||
19 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
20 | |||
21 | /** | ||
22 | * Global registry of active VIATRA Query Engines. | ||
23 | * | ||
24 | * <p> | ||
25 | * Manages an {@link ViatraQueryEngine} for each model (more precisely scope), that is created on demand. Managed engines are shared between | ||
26 | * clients querying the same model. | ||
27 | * | ||
28 | * <p> | ||
29 | * It is also possible to create private, unmanaged engines that are not shared between clients. | ||
30 | * | ||
31 | * <p> | ||
32 | * Only weak references are retained on the managed engines. So if there are no other references to the matchers or the | ||
33 | * engine, they can eventually be GC'ed, and they won't block the model from being GC'ed either. | ||
34 | * | ||
35 | * | ||
36 | * @author Bergmann Gabor | ||
37 | * | ||
38 | */ | ||
39 | public class ViatraQueryEngineManager { | ||
40 | private static ViatraQueryEngineManager instance = new ViatraQueryEngineManager(); | ||
41 | |||
42 | |||
43 | /** | ||
44 | * @return the singleton instance | ||
45 | */ | ||
46 | public static ViatraQueryEngineManager getInstance() { | ||
47 | return instance; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * The engine manager keeps track of the managed engines via weak references only, so it will not keep unused | ||
52 | * managed engines from being garbage collected. Still, as long as the user keeps the model in memory, the | ||
53 | * associated managed engine will not be garbage collected, as it is expected to be strongly reachable from the | ||
54 | * model via the scope-specific base index or change notification listeners (see | ||
55 | * {@link BaseIndexListener#iqEngine}). | ||
56 | */ | ||
57 | Map<QueryScope, WeakReference<ViatraQueryEngineImpl>> engines; | ||
58 | |||
59 | ViatraQueryEngineManager() { | ||
60 | super(); | ||
61 | engines = new WeakHashMap<QueryScope, WeakReference<ViatraQueryEngineImpl>>(); | ||
62 | initializationListeners = new HashSet<ViatraQueryEngineInitializationListener>(); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Creates a managed VIATRA Query Engine at a given scope (e.g. an EMF Resource or ResourceSet, as in {@link EMFScope}) | ||
67 | * or retrieves an already existing one. Repeated invocations for a single model root will return the same engine. | ||
68 | * Consequently, the engine will be reused between different clients querying the same model, providing performance benefits. | ||
69 | * | ||
70 | * <p> | ||
71 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
72 | * | ||
73 | * @param scope | ||
74 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
75 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
76 | * @return a new or previously existing engine | ||
77 | */ | ||
78 | public ViatraQueryEngine getQueryEngine(QueryScope scope) { | ||
79 | return getQueryEngine(scope, ViatraQueryEngineOptions.getDefault()); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Creates a managed VIATRA Query Engine at a given scope (e.g. an EMF Resource or ResourceSet, as in {@link EMFScope}) | ||
84 | * or retrieves an already existing one. Repeated invocations for a single model root will return the same engine. | ||
85 | * Consequently, the engine will be reused between different clients querying the same model, providing performance benefits. | ||
86 | * | ||
87 | * <p> | ||
88 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
89 | * | ||
90 | * @param scope | ||
91 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
92 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
93 | * @return a new or previously existing engine | ||
94 | * @since 1.4 | ||
95 | */ | ||
96 | public ViatraQueryEngine getQueryEngine(QueryScope scope, ViatraQueryEngineOptions options) { | ||
97 | ViatraQueryEngineImpl engine = getEngineInternal(scope); | ||
98 | if (engine == null) { | ||
99 | engine = new ViatraQueryEngineImpl(this, scope, options); | ||
100 | engines.put(scope, new WeakReference<ViatraQueryEngineImpl>(engine)); | ||
101 | notifyInitializationListeners(engine); | ||
102 | } | ||
103 | return engine; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Retrieves an already existing managed VIATRA Query Engine. | ||
108 | * | ||
109 | * @param scope | ||
110 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
111 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
112 | * @return a previously existing engine, or null if no engine is active for the given EMF model root | ||
113 | */ | ||
114 | public ViatraQueryEngine getQueryEngineIfExists(QueryScope scope) { | ||
115 | return getEngineInternal(scope); | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * Collects all {@link ViatraQueryEngine} instances that still exist. | ||
120 | * | ||
121 | * @return set of engines if there is any, otherwise EMTPY_SET | ||
122 | */ | ||
123 | public Set<ViatraQueryEngine> getExistingQueryEngines(){ | ||
124 | Set<ViatraQueryEngine> existingEngines = null; | ||
125 | for (WeakReference<ViatraQueryEngineImpl> engineRef : engines.values()) { | ||
126 | AdvancedViatraQueryEngine engine = engineRef == null ? null : engineRef.get(); | ||
127 | if(existingEngines == null) { | ||
128 | existingEngines = new HashSet<>(); | ||
129 | } | ||
130 | existingEngines.add(engine); | ||
131 | } | ||
132 | if(existingEngines == null) { | ||
133 | existingEngines = Collections.emptySet(); | ||
134 | } | ||
135 | return existingEngines; | ||
136 | } | ||
137 | |||
138 | private final Set<ViatraQueryEngineInitializationListener> initializationListeners; | ||
139 | |||
140 | /** | ||
141 | * Registers a listener for new engine initialization. | ||
142 | * | ||
143 | * <p/> For removal, use {@link #removeQueryEngineInitializationListener} | ||
144 | * | ||
145 | * @param listener the listener to register | ||
146 | */ | ||
147 | public void addQueryEngineInitializationListener(ViatraQueryEngineInitializationListener listener) { | ||
148 | checkArgument(listener != null, "Cannot add null listener!"); | ||
149 | initializationListeners.add(listener); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Removes a registered listener added with {@link #addQueryEngineInitializationListener} | ||
154 | * | ||
155 | * @param listener | ||
156 | */ | ||
157 | public void removeQueryEngineInitializationListener(ViatraQueryEngineInitializationListener listener) { | ||
158 | checkArgument(listener != null, "Cannot remove null listener!"); | ||
159 | initializationListeners.remove(listener); | ||
160 | } | ||
161 | |||
162 | /** | ||
163 | * Notifies listeners that a new engine has been initialized. | ||
164 | * | ||
165 | * @param engine the initialized engine | ||
166 | */ | ||
167 | protected void notifyInitializationListeners(AdvancedViatraQueryEngine engine) { | ||
168 | try { | ||
169 | if (!initializationListeners.isEmpty()) { | ||
170 | for (ViatraQueryEngineInitializationListener listener : new HashSet<>(initializationListeners)) { | ||
171 | listener.engineInitialized(engine); | ||
172 | } | ||
173 | } | ||
174 | } catch (Exception ex) { | ||
175 | ViatraQueryLoggingUtil.getLogger(getClass()).fatal( | ||
176 | "VIATRA Query Engine Manager encountered an error in delivering notifications" | ||
177 | + " about engine initialization. ", ex); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * @param emfRoot | ||
183 | * @return | ||
184 | */ | ||
185 | private ViatraQueryEngineImpl getEngineInternal(QueryScope scope) { | ||
186 | final WeakReference<ViatraQueryEngineImpl> engineRef = engines.get(scope); | ||
187 | ViatraQueryEngineImpl engine = engineRef == null ? null : engineRef.get(); | ||
188 | return engine; | ||
189 | } | ||
190 | |||
191 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineOptions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineOptions.java new file mode 100644 index 00000000..15bf1f91 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineOptions.java | |||
@@ -0,0 +1,293 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Balázs Grill, Zoltan Ujhelyi, 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.api; | ||
10 | |||
11 | |||
12 | import java.util.Objects; | ||
13 | import java.util.ServiceLoader; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | |||
20 | /** | ||
21 | * This class is intended to provide options to a created {@link ViatraQueryEngine} instance. The {@link #DEFAULT} | ||
22 | * instance represents the configuration that is selected when no explicit options are provided by the user. To create | ||
23 | * new configurations, use the static builder methods {@link #defineOptions()} (starts with empty options) or | ||
24 | * {@link #copyOptions(ViatraQueryEngineOptions)} (starts with all options from an existing configuration). | ||
25 | * | ||
26 | * @author Balázs Grill, Zoltan Ujhelyi | ||
27 | * @since 1.4 | ||
28 | * | ||
29 | */ | ||
30 | public final class ViatraQueryEngineOptions { | ||
31 | |||
32 | private static boolean areSystemDefaultsCalculated = false; | ||
33 | private static IQueryBackendFactory systemDefaultBackendFactory; | ||
34 | private static IQueryBackendFactory systemDefaultCachingBackendFactory; | ||
35 | private static IQueryBackendFactory systemDefaultSearchBackendFactory; | ||
36 | |||
37 | /** | ||
38 | * @since 2.0 | ||
39 | */ | ||
40 | public static void setSystemDefaultBackends(IQueryBackendFactory systemDefaultBackendFactory, | ||
41 | IQueryBackendFactory systemDefaultCachingBackendFactory, | ||
42 | IQueryBackendFactory systemDefaultSearchBackendFactory) { | ||
43 | areSystemDefaultsCalculated = true; | ||
44 | ViatraQueryEngineOptions.systemDefaultBackendFactory = systemDefaultBackendFactory; | ||
45 | ViatraQueryEngineOptions.systemDefaultCachingBackendFactory = systemDefaultCachingBackendFactory; | ||
46 | ViatraQueryEngineOptions.systemDefaultSearchBackendFactory = systemDefaultSearchBackendFactory; | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * If {@link #setSystemDefaultBackends(IQueryBackendFactory, IQueryBackendFactory, IQueryBackendFactory)} is not | ||
51 | * called, this method is responsible of finding the corresponding backends on the classpath using Java Service | ||
52 | * loaders. | ||
53 | */ | ||
54 | private static void calculateSystemDefaultBackends() { | ||
55 | for (IQueryBackendFactoryProvider provider : ServiceLoader.load(IQueryBackendFactoryProvider.class)) { | ||
56 | if (provider.isSystemDefaultEngine()) { | ||
57 | systemDefaultBackendFactory = provider.getFactory(); | ||
58 | } | ||
59 | if (provider.isSystemDefaultCachingBackend()) { | ||
60 | systemDefaultCachingBackendFactory = provider.getFactory(); | ||
61 | } | ||
62 | if (provider.isSystemDefaultSearchBackend()) { | ||
63 | systemDefaultSearchBackendFactory = provider.getFactory(); | ||
64 | } | ||
65 | } | ||
66 | areSystemDefaultsCalculated = true; | ||
67 | } | ||
68 | |||
69 | private static IQueryBackendFactory getSystemDefaultBackend() { | ||
70 | if (!areSystemDefaultsCalculated) { | ||
71 | calculateSystemDefaultBackends(); | ||
72 | } | ||
73 | return Objects.requireNonNull(systemDefaultBackendFactory, "System default backend not found"); | ||
74 | } | ||
75 | |||
76 | private static IQueryBackendFactory getSystemDefaultCachingBackend() { | ||
77 | if (!areSystemDefaultsCalculated) { | ||
78 | calculateSystemDefaultBackends(); | ||
79 | } | ||
80 | return Objects.requireNonNull(systemDefaultCachingBackendFactory, "System default caching backend not found"); | ||
81 | } | ||
82 | |||
83 | private static IQueryBackendFactory getSystemDefaultSearchBackend() { | ||
84 | if (!areSystemDefaultsCalculated) { | ||
85 | calculateSystemDefaultBackends(); | ||
86 | } | ||
87 | return Objects.requireNonNull(systemDefaultSearchBackendFactory, "System default search backend not found"); | ||
88 | } | ||
89 | |||
90 | private final QueryEvaluationHint engineDefaultHints; | ||
91 | |||
92 | private final IQueryBackendFactory defaultCachingBackendFactory; | ||
93 | private final IQueryBackendFactory defaultSearchBackendFactory; | ||
94 | |||
95 | /** The default engine options; if options are not defined, this version will be used. */ | ||
96 | private static ViatraQueryEngineOptions DEFAULT; | ||
97 | |||
98 | /** | ||
99 | * @since 2.0 | ||
100 | */ | ||
101 | public static final ViatraQueryEngineOptions getDefault() { | ||
102 | if (DEFAULT == null) { | ||
103 | DEFAULT = new Builder().build(); | ||
104 | } | ||
105 | return DEFAULT; | ||
106 | } | ||
107 | |||
108 | public static final class Builder { | ||
109 | private QueryEvaluationHint engineDefaultHints; | ||
110 | |||
111 | private IQueryBackendFactory defaultBackendFactory; | ||
112 | private IQueryBackendFactory defaultCachingBackendFactory; | ||
113 | private IQueryBackendFactory defaultSearchBackendFactory; | ||
114 | |||
115 | public Builder() { | ||
116 | |||
117 | } | ||
118 | |||
119 | public Builder(ViatraQueryEngineOptions from) { | ||
120 | this.engineDefaultHints = from.engineDefaultHints; | ||
121 | this.defaultBackendFactory = engineDefaultHints.getQueryBackendFactory(); | ||
122 | this.defaultCachingBackendFactory = from.defaultCachingBackendFactory; | ||
123 | this.defaultSearchBackendFactory = from.defaultSearchBackendFactory; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Note that the backend factory in the hint is overridden by a factory added with | ||
128 | * {@link #withDefaultBackend(IQueryBackendFactory)}. | ||
129 | */ | ||
130 | public Builder withDefaultHint(QueryEvaluationHint engineDefaultHints) { | ||
131 | this.engineDefaultHints = engineDefaultHints; | ||
132 | return this; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Note that this backend factory overrides the factory defined by the hint added by | ||
137 | * {@link #withDefaultHint(QueryEvaluationHint)}. | ||
138 | */ | ||
139 | public Builder withDefaultBackend(IQueryBackendFactory defaultBackendFactory) { | ||
140 | this.defaultBackendFactory = defaultBackendFactory; | ||
141 | return this; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * @since 2.0 | ||
146 | */ | ||
147 | public Builder withDefaultSearchBackend(IQueryBackendFactory defaultSearchBackendFactory) { | ||
148 | Preconditions.checkArgument(!defaultSearchBackendFactory.isCaching(), "%s is not a search backend", defaultSearchBackendFactory.getClass()); | ||
149 | this.defaultSearchBackendFactory = defaultSearchBackendFactory; | ||
150 | return this; | ||
151 | } | ||
152 | |||
153 | public Builder withDefaultCachingBackend(IQueryBackendFactory defaultCachingBackendFactory) { | ||
154 | Preconditions.checkArgument(defaultCachingBackendFactory.isCaching(), "%s is not a caching backend", defaultCachingBackendFactory.getClass()); | ||
155 | this.defaultCachingBackendFactory = defaultCachingBackendFactory; | ||
156 | return this; | ||
157 | } | ||
158 | |||
159 | public ViatraQueryEngineOptions build() { | ||
160 | IQueryBackendFactory defaultFactory = getDefaultBackend(); | ||
161 | QueryEvaluationHint hint = getEngineDefaultHints(defaultFactory); | ||
162 | return new ViatraQueryEngineOptions(hint, getDefaultCachingBackend(), getDefaultSearchBackend()); | ||
163 | } | ||
164 | |||
165 | private IQueryBackendFactory getDefaultBackend() { | ||
166 | if (defaultBackendFactory != null){ | ||
167 | return defaultBackendFactory; | ||
168 | } else if (engineDefaultHints != null) { | ||
169 | return engineDefaultHints.getQueryBackendFactory(); | ||
170 | } else { | ||
171 | return getSystemDefaultBackend(); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | private IQueryBackendFactory getDefaultCachingBackend() { | ||
176 | if (defaultCachingBackendFactory != null) { | ||
177 | return defaultCachingBackendFactory; | ||
178 | } else if (defaultBackendFactory != null && defaultBackendFactory.isCaching()) { | ||
179 | return defaultBackendFactory; | ||
180 | } else { | ||
181 | return getSystemDefaultCachingBackend(); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | private IQueryBackendFactory getDefaultSearchBackend() { | ||
186 | if (defaultSearchBackendFactory != null) { | ||
187 | return defaultSearchBackendFactory; | ||
188 | } else if (defaultBackendFactory != null && !defaultBackendFactory.isCaching()) { | ||
189 | return defaultBackendFactory; | ||
190 | } else { | ||
191 | return getSystemDefaultSearchBackend(); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | private QueryEvaluationHint getEngineDefaultHints(IQueryBackendFactory defaultFactory) { | ||
196 | if (engineDefaultHints != null){ | ||
197 | return engineDefaultHints.overrideBy(new QueryEvaluationHint(null, defaultFactory)); | ||
198 | } else { | ||
199 | return new QueryEvaluationHint(null, defaultFactory); | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * Initializes an option builder with no previously set options. | ||
206 | */ | ||
207 | public static Builder defineOptions() { | ||
208 | return new Builder(); | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * Initializes an option builder with settings from an existing configuration. | ||
213 | */ | ||
214 | public static Builder copyOptions(ViatraQueryEngineOptions options) { | ||
215 | return new Builder(options); | ||
216 | } | ||
217 | |||
218 | private ViatraQueryEngineOptions(QueryEvaluationHint engineDefaultHints, | ||
219 | IQueryBackendFactory defaultCachingBackendFactory, IQueryBackendFactory defaultSearchBackendFactory) { | ||
220 | this.engineDefaultHints = engineDefaultHints; | ||
221 | this.defaultCachingBackendFactory = defaultCachingBackendFactory; | ||
222 | this.defaultSearchBackendFactory = defaultSearchBackendFactory; | ||
223 | } | ||
224 | |||
225 | public QueryEvaluationHint getEngineDefaultHints() { | ||
226 | return engineDefaultHints; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Returns the configured default backend | ||
231 | * | ||
232 | * @return the defaultBackendFactory | ||
233 | */ | ||
234 | public IQueryBackendFactory getDefaultBackendFactory() { | ||
235 | switch (engineDefaultHints.getQueryBackendRequirementType()) { | ||
236 | case DEFAULT_CACHING: | ||
237 | return ViatraQueryEngineOptions.getSystemDefaultCachingBackend(); | ||
238 | case DEFAULT_SEARCH: | ||
239 | return ViatraQueryEngineOptions.getSystemDefaultCachingBackend(); | ||
240 | case SPECIFIC: | ||
241 | return engineDefaultHints.getQueryBackendFactory(); | ||
242 | case UNSPECIFIED: | ||
243 | default: | ||
244 | return ViatraQueryEngineOptions.getSystemDefaultBackend(); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Returns the configured default caching backend. If the default backend caches matches, it is usually expected, but | ||
250 | * not mandatory for the two default backends to be the same. | ||
251 | */ | ||
252 | public IQueryBackendFactory getDefaultCachingBackendFactory() { | ||
253 | return defaultCachingBackendFactory; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Returns the configured default search-based backend. If the default backend is search-based, it is usually expected, but | ||
258 | * not mandatory for the two default backends to be the same. | ||
259 | * @since 2.0 | ||
260 | */ | ||
261 | public IQueryBackendFactory getDefaultSearchBackendFactory() { | ||
262 | return defaultSearchBackendFactory; | ||
263 | } | ||
264 | |||
265 | @Override | ||
266 | public String toString() { | ||
267 | // TODO defaultCachingBackendFactory is ignored | ||
268 | if(Objects.equals(engineDefaultHints, DEFAULT.engineDefaultHints)) | ||
269 | return "defaults"; | ||
270 | else | ||
271 | return engineDefaultHints.toString(); | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * @since 2.0 | ||
276 | */ | ||
277 | public IQueryBackendFactory getQueryBackendFactory(QueryEvaluationHint hint) { | ||
278 | if (hint == null) { | ||
279 | return getDefaultBackendFactory(); | ||
280 | } | ||
281 | |||
282 | switch (hint.getQueryBackendRequirementType()) { | ||
283 | case DEFAULT_CACHING: | ||
284 | return getDefaultCachingBackendFactory(); | ||
285 | case DEFAULT_SEARCH: | ||
286 | return getDefaultSearchBackendFactory(); | ||
287 | case SPECIFIC: | ||
288 | return hint.getQueryBackendFactory(); | ||
289 | default: | ||
290 | return getDefaultBackendFactory(); | ||
291 | } | ||
292 | } | ||
293 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryMatcher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryMatcher.java new file mode 100644 index 00000000..1ae0c96f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryMatcher.java | |||
@@ -0,0 +1,258 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Optional; | ||
15 | import java.util.Set; | ||
16 | import java.util.function.Consumer; | ||
17 | import java.util.stream.Stream; | ||
18 | |||
19 | /** | ||
20 | * Interface for a VIATRA Query matcher associated with a graph pattern. | ||
21 | * | ||
22 | * @param <Match> | ||
23 | * the IPatternMatch type representing a single match of this pattern. | ||
24 | * @author Bergmann Gábor | ||
25 | * @noimplement This interface is not intended to be implemented by clients. Implement BaseMatcher instead. | ||
26 | */ | ||
27 | public interface ViatraQueryMatcher<Match extends IPatternMatch> { | ||
28 | // REFLECTION | ||
29 | /** The pattern that will be matched. */ | ||
30 | IQuerySpecification<? extends ViatraQueryMatcher<Match>> getSpecification(); | ||
31 | |||
32 | /** Fully qualified name of the pattern. */ | ||
33 | String getPatternName(); | ||
34 | |||
35 | /** Returns the index of the symbolic parameter with the given name. */ | ||
36 | Integer getPositionOfParameter(String parameterName); | ||
37 | |||
38 | /** Returns the array of symbolic parameter names. */ | ||
39 | List<String> getParameterNames(); | ||
40 | |||
41 | // ALL MATCHES | ||
42 | /** | ||
43 | * Returns the set of all pattern matches. | ||
44 | * | ||
45 | * @return matches represented as a Match object. | ||
46 | */ | ||
47 | Collection<Match> getAllMatches(); | ||
48 | |||
49 | /** | ||
50 | * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. | ||
51 | * | ||
52 | * @param partialMatch | ||
53 | * a partial match of the pattern where each non-null field binds the corresponding pattern parameter to | ||
54 | * a fixed value. | ||
55 | * @return matches represented as a Match object. | ||
56 | */ | ||
57 | Collection<Match> getAllMatches(Match partialMatch); | ||
58 | |||
59 | /** | ||
60 | * Returns a stream of all pattern matches. | ||
61 | * <p> | ||
62 | * <strong>WARNING</strong> If the result set changes while the stream is evaluated, the set of matches included in | ||
63 | * the stream are unspecified. In such cases, either rely on {@link #getAllMatches()} or collect the results of the | ||
64 | * stream in end-user code. | ||
65 | * | ||
66 | * @return matches represented as a Match object. | ||
67 | * @since 2.0 | ||
68 | */ | ||
69 | Stream<Match> streamAllMatches(); | ||
70 | |||
71 | /** | ||
72 | * Returns a stream of all matches of the pattern that conform to the given fixed values of some parameters. | ||
73 | * <p> | ||
74 | * <strong>WARNING</strong> If the result set changes while the stream is evaluated, the set of matches included in | ||
75 | * the stream are unspecified. In such cases, either rely on {@link #getAllMatches()} or collect the results of the | ||
76 | * stream in end-user code. | ||
77 | * | ||
78 | * @param partialMatch | ||
79 | * a partial match of the pattern where each non-null field binds the corresponding pattern parameter to | ||
80 | * a fixed value. | ||
81 | * @return matches represented as a Match object. | ||
82 | * @since 2.0 | ||
83 | */ | ||
84 | Stream<Match> streamAllMatches(Match partialMatch); | ||
85 | |||
86 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
87 | |||
88 | // SINGLE MATCH | ||
89 | /** | ||
90 | * Returns an arbitrarily chosen pattern match. Neither determinism nor randomness of selection is guaranteed. | ||
91 | * | ||
92 | * @return a match represented as a Match object, or an empty Optional if no match is found. | ||
93 | * @since 2.0 | ||
94 | */ | ||
95 | Optional<Match> getOneArbitraryMatch(); | ||
96 | |||
97 | /** | ||
98 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
99 | * Neither determinism nor randomness of selection is guaranteed. | ||
100 | * | ||
101 | * @param partialMatch | ||
102 | * a partial match of the pattern where each non-null field binds the corresponding pattern parameter to | ||
103 | * a fixed value. | ||
104 | * @return a match represented as a Match object, or an empty Optional if no match is found. | ||
105 | * @since 2.0 | ||
106 | */ | ||
107 | Optional<Match> getOneArbitraryMatch(Match partialMatch); | ||
108 | |||
109 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
110 | |||
111 | // MATCH CHECKING | ||
112 | /** | ||
113 | * Indicates whether the query has any kind of matches. | ||
114 | * | ||
115 | * @return true if there exists a valid match of the pattern. | ||
116 | * @since 1.7 | ||
117 | */ | ||
118 | boolean hasMatch(); | ||
119 | |||
120 | /** | ||
121 | * Indicates whether the given combination of specified pattern parameters constitute a valid pattern match, under | ||
122 | * any possible substitution of the unspecified parameters (if any). | ||
123 | * | ||
124 | * @param partialMatch | ||
125 | * a (partial) match of the pattern where each non-null field binds the corresponding pattern parameter | ||
126 | * to a fixed value. | ||
127 | * @return true if the input is a valid (partial) match of the pattern. | ||
128 | */ | ||
129 | boolean hasMatch(Match partialMatch); | ||
130 | |||
131 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
132 | |||
133 | // NUMBER OF MATCHES | ||
134 | /** | ||
135 | * Returns the number of all pattern matches. | ||
136 | * | ||
137 | * @return the number of pattern matches found. | ||
138 | */ | ||
139 | int countMatches(); | ||
140 | |||
141 | /** | ||
142 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
143 | * | ||
144 | * @param partialMatch | ||
145 | * a partial match of the pattern where each non-null field binds the corresponding pattern parameter to | ||
146 | * a fixed value. | ||
147 | * @return the number of pattern matches found. | ||
148 | */ | ||
149 | int countMatches(Match partialMatch); | ||
150 | |||
151 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
152 | |||
153 | // FOR EACH MATCH | ||
154 | /** | ||
155 | * Executes the given processor on each match of the pattern. | ||
156 | * | ||
157 | * @param processor | ||
158 | * the action that will process each pattern match. | ||
159 | * @since 2.0 | ||
160 | */ | ||
161 | void forEachMatch(Consumer<? super Match> processor); | ||
162 | |||
163 | /** | ||
164 | * Executes the given processor on each match of the pattern that conforms to the given fixed values of some | ||
165 | * parameters. | ||
166 | * | ||
167 | * @param partialMatch | ||
168 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
169 | * @param processor | ||
170 | * the action that will process each pattern match. | ||
171 | * @since 2.0 | ||
172 | */ | ||
173 | void forEachMatch(Match partialMatch, Consumer<? super Match> processor); | ||
174 | |||
175 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
176 | |||
177 | // FOR ONE ARBITRARY MATCH | ||
178 | /** | ||
179 | * Executes the given processor on an arbitrarily chosen match of the pattern. Neither determinism nor randomness of | ||
180 | * selection is guaranteed. | ||
181 | * | ||
182 | * @param processor | ||
183 | * the action that will process the selected match. | ||
184 | * @return true if the pattern has at least one match, false if the processor was not invoked | ||
185 | * @since 2.0 | ||
186 | */ | ||
187 | boolean forOneArbitraryMatch(Consumer<? super Match> processor); | ||
188 | |||
189 | /** | ||
190 | * Executes the given processor on an arbitrarily chosen match of the pattern that conforms to the given fixed | ||
191 | * values of some parameters. Neither determinism nor randomness of selection is guaranteed. | ||
192 | * | ||
193 | * @param partialMatch | ||
194 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
195 | * @param processor | ||
196 | * the action that will process the selected match. | ||
197 | * @return true if the pattern has at least one match with the given parameter values, false if the processor was | ||
198 | * not invoked | ||
199 | * @since 2.0 | ||
200 | */ | ||
201 | boolean forOneArbitraryMatch(Match partialMatch, Consumer<? super Match> processor); | ||
202 | |||
203 | // variant(s) with input binding as pattern-specific parameters: not declared in interface | ||
204 | |||
205 | /** | ||
206 | * Returns an empty, mutable Match for the matcher. | ||
207 | * Fields of the mutable match can be filled to create a partial match, usable as matcher input. | ||
208 | * This can be used to call the matcher with a partial match | ||
209 | * even if the specific class of the matcher or the match is unknown. | ||
210 | * | ||
211 | * @return the empty match | ||
212 | */ | ||
213 | Match newEmptyMatch(); | ||
214 | |||
215 | /** | ||
216 | * Returns a new (partial) Match object for the matcher. | ||
217 | * This can be used e.g. to call the matcher with a partial | ||
218 | * match. | ||
219 | * | ||
220 | * <p>The returned match will be immutable. Use {@link #newEmptyMatch()} to obtain a mutable match object. | ||
221 | * | ||
222 | * @param parameters | ||
223 | * the fixed value of pattern parameters, or null if not bound. | ||
224 | * @return the (partial) match object. | ||
225 | */ | ||
226 | Match newMatch(Object... parameters); | ||
227 | |||
228 | /** | ||
229 | * Retrieve the set of values that occur in matches for the given parameterName. | ||
230 | * | ||
231 | * @param parameterName | ||
232 | * name of the parameter for which values are returned | ||
233 | * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists, | ||
234 | * empty set if there are no matches | ||
235 | */ | ||
236 | Set<Object> getAllValues(final String parameterName); | ||
237 | |||
238 | /** | ||
239 | * Retrieve the set of values that occur in matches for the given parameterName, that conforms to the given fixed | ||
240 | * values of some parameters. | ||
241 | * | ||
242 | * @param parameterName | ||
243 | * name of the parameter for which values are returned | ||
244 | * @param partialMatch | ||
245 | * a partial match of the pattern where each non-null field binds the corresponding pattern parameter to | ||
246 | * a fixed value. | ||
247 | * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists | ||
248 | * or if the parameter with the given name is set in partialMatch, empty set if there are no matches | ||
249 | */ | ||
250 | Set<Object> getAllValues(final String parameterName, Match partialMatch); | ||
251 | |||
252 | /** | ||
253 | * Returns the engine that the matcher uses. | ||
254 | * | ||
255 | * @return the engine | ||
256 | */ | ||
257 | ViatraQueryEngine getEngine(); | ||
258 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryModelUpdateListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryModelUpdateListener.java new file mode 100644 index 00000000..da8bf87e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryModelUpdateListener.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.api; | ||
10 | |||
11 | |||
12 | |||
13 | /** | ||
14 | * Listener interface for model changes affecting different levels of the VIATRA Query architecture. | ||
15 | * | ||
16 | * @author Abel Hegedus | ||
17 | * | ||
18 | */ | ||
19 | public interface ViatraQueryModelUpdateListener { | ||
20 | |||
21 | /** | ||
22 | * Possible notification levels for changes | ||
23 | * | ||
24 | * @author Abel Hegedus | ||
25 | * | ||
26 | */ | ||
27 | enum ChangeLevel { | ||
28 | NO_CHANGE, MODEL, INDEX, MATCHSET; | ||
29 | |||
30 | public ChangeLevel changeOccured(ChangeLevel occuredLevel) { | ||
31 | if(this.compareTo(occuredLevel) < 0) { | ||
32 | return occuredLevel; | ||
33 | } else { | ||
34 | return this; | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | /** | ||
39 | * Called after each change with also sending the level of change. | ||
40 | * Only called if the change level is at least at the level returned by getLevel(). | ||
41 | * | ||
42 | * @param changeLevel | ||
43 | */ | ||
44 | void notifyChanged(ChangeLevel changeLevel); | ||
45 | |||
46 | /** | ||
47 | * This may be queried only ONCE (!!!) at the registration of the listener. | ||
48 | * | ||
49 | * NOTE: this allows us to only create engine level change providers if there is someone who needs it. | ||
50 | * | ||
51 | * @return the change level where you want notifications | ||
52 | */ | ||
53 | ChangeLevel getLevel(); | ||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedPatternGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedPatternGroup.java new file mode 100644 index 00000000..2e2a823b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedPatternGroup.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Mark Czotter, Istvan Rath and Daniel Varro | ||
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.api.impl; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
15 | |||
16 | /** | ||
17 | * @author Mark Czotter | ||
18 | * | ||
19 | */ | ||
20 | public abstract class BaseGeneratedPatternGroup extends BaseQueryGroup { | ||
21 | |||
22 | @Override | ||
23 | public Set<IQuerySpecification<?>> getSpecifications() { | ||
24 | return querySpecifications; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * Returns {@link IQuerySpecification} objects for handling them as a group. To be filled by constructors of subclasses. | ||
29 | */ | ||
30 | protected Set<IQuerySpecification<?>> querySpecifications = new HashSet<IQuerySpecification<?>>(); | ||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseMatcher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseMatcher.java new file mode 100644 index 00000000..ad79aafd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseMatcher.java | |||
@@ -0,0 +1,350 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api.impl; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Optional; | ||
15 | import java.util.Set; | ||
16 | import java.util.function.Consumer; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
20 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
21 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
22 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
23 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
24 | import tools.refinery.viatra.runtime.internal.apiimpl.QueryResultWrapper; | ||
25 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
26 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
27 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
28 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
29 | |||
30 | /** | ||
31 | * Base implementation of ViatraQueryMatcher. | ||
32 | * | ||
33 | * @author Bergmann Gábor | ||
34 | * | ||
35 | * @param <Match> | ||
36 | */ | ||
37 | public abstract class BaseMatcher<Match extends IPatternMatch> extends QueryResultWrapper implements ViatraQueryMatcher<Match> { | ||
38 | |||
39 | // FIELDS AND CONSTRUCTOR | ||
40 | |||
41 | protected ViatraQueryEngine engine; | ||
42 | protected IQuerySpecification<? extends BaseMatcher<Match>> querySpecification; | ||
43 | private IMatcherCapability capabilities; | ||
44 | |||
45 | /** | ||
46 | * @since 1.4 | ||
47 | */ | ||
48 | public BaseMatcher(IQuerySpecification<? extends BaseMatcher<Match>> querySpecification) { | ||
49 | this.querySpecification = querySpecification; | ||
50 | this.querySpecification.getInternalQueryRepresentation().ensureInitialized(); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 1.4 | ||
55 | */ | ||
56 | @Override | ||
57 | protected | ||
58 | void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider, IMatcherCapability capabilities){ | ||
59 | this.backend = resultProvider; | ||
60 | this.engine = engine; | ||
61 | this.capabilities = capabilities; | ||
62 | } | ||
63 | |||
64 | // ARRAY-BASED INTERFACE | ||
65 | |||
66 | /** Converts the array representation of a pattern match to an immutable Match object. */ | ||
67 | protected abstract Match arrayToMatch(Object[] parameters); | ||
68 | /** Converts the array representation of a pattern match to a mutable Match object. */ | ||
69 | protected abstract Match arrayToMatchMutable(Object[] parameters); | ||
70 | |||
71 | /** Converts the Match object of a pattern match to the array representation. */ | ||
72 | protected Object[] matchToArray(Match partialMatch) { | ||
73 | return partialMatch.toArray(); | ||
74 | } | ||
75 | // TODO make me public for performance reasons | ||
76 | protected abstract Match tupleToMatch(Tuple t); | ||
77 | |||
78 | private Object[] fEmptyArray; | ||
79 | |||
80 | protected Object[] emptyArray() { | ||
81 | if (fEmptyArray == null) | ||
82 | fEmptyArray = new Object[getSpecification().getParameterNames().size()]; | ||
83 | return fEmptyArray; | ||
84 | } | ||
85 | |||
86 | // REFLECTION | ||
87 | |||
88 | @Override | ||
89 | public Integer getPositionOfParameter(String parameterName) { | ||
90 | return getSpecification().getPositionOfParameter(parameterName); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public List<String> getParameterNames() { | ||
95 | return getSpecification().getParameterNames(); | ||
96 | } | ||
97 | |||
98 | // BASE IMPLEMENTATION | ||
99 | |||
100 | @Override | ||
101 | public Collection<Match> getAllMatches() { | ||
102 | return rawStreamAllMatches(emptyArray()).collect(Collectors.toSet()); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Stream<Match> streamAllMatches() { | ||
107 | return rawStreamAllMatches(emptyArray()); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Returns a stream of all matches of the pattern that conform to the given fixed values of some parameters. | ||
112 | * | ||
113 | * @param parameters | ||
114 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
115 | * @pre size of input array must be equal to the number of parameters. | ||
116 | * @return matches represented as a Match object. | ||
117 | * @since 2.0 | ||
118 | */ | ||
119 | protected Stream<Match> rawStreamAllMatches(Object[] parameters) { | ||
120 | // clones the tuples into a match object to protect the Tuples from modifications outside of the ReteMatcher | ||
121 | return backend.getAllMatches(parameters).map(this::tupleToMatch); | ||
122 | } | ||
123 | |||
124 | @Override | ||
125 | public Collection<Match> getAllMatches(Match partialMatch) { | ||
126 | return rawStreamAllMatches(partialMatch.toArray()).collect(Collectors.toSet()); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public Stream<Match> streamAllMatches(Match partialMatch) { | ||
131 | return rawStreamAllMatches(partialMatch.toArray()); | ||
132 | } | ||
133 | |||
134 | // with input binding as pattern-specific parameters: not declared in interface | ||
135 | |||
136 | @Override | ||
137 | public Optional<Match> getOneArbitraryMatch() { | ||
138 | return rawGetOneArbitraryMatch(emptyArray()); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
143 | * Neither determinism nor randomness of selection is guaranteed. | ||
144 | * | ||
145 | * @param parameters | ||
146 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
147 | * @pre size of input array must be equal to the number of parameters. | ||
148 | * @return a match represented as a Match object, or null if no match is found. | ||
149 | * @since 2.0 | ||
150 | */ | ||
151 | protected Optional<Match> rawGetOneArbitraryMatch(Object[] parameters) { | ||
152 | return backend.getOneArbitraryMatch(parameters).map(this::tupleToMatch); | ||
153 | } | ||
154 | |||
155 | @Override | ||
156 | public Optional<Match> getOneArbitraryMatch(Match partialMatch) { | ||
157 | return rawGetOneArbitraryMatch(partialMatch.toArray()); | ||
158 | } | ||
159 | |||
160 | // with input binding as pattern-specific parameters: not declared in interface | ||
161 | |||
162 | /** | ||
163 | * Indicates whether the given combination of specified pattern parameters constitute a valid pattern match, under | ||
164 | * any possible substitution of the unspecified parameters. | ||
165 | * | ||
166 | * @param parameters | ||
167 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
168 | * @return true if the input is a valid (partial) match of the pattern. | ||
169 | */ | ||
170 | protected boolean rawHasMatch(Object[] parameters) { | ||
171 | return backend.hasMatch(parameters); | ||
172 | } | ||
173 | |||
174 | @Override | ||
175 | public boolean hasMatch() { | ||
176 | return rawHasMatch(emptyArray()); | ||
177 | } | ||
178 | |||
179 | @Override | ||
180 | public boolean hasMatch(Match partialMatch) { | ||
181 | return rawHasMatch(partialMatch.toArray()); | ||
182 | } | ||
183 | |||
184 | // with input binding as pattern-specific parameters: not declared in interface | ||
185 | |||
186 | @Override | ||
187 | public int countMatches() { | ||
188 | return rawCountMatches(emptyArray()); | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
193 | * | ||
194 | * @param parameters | ||
195 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
196 | * @pre size of input array must be equal to the number of parameters. | ||
197 | * @return the number of pattern matches found. | ||
198 | */ | ||
199 | protected int rawCountMatches(Object[] parameters) { | ||
200 | return backend.countMatches(parameters); | ||
201 | } | ||
202 | |||
203 | @Override | ||
204 | public int countMatches(Match partialMatch) { | ||
205 | return rawCountMatches(partialMatch.toArray()); | ||
206 | } | ||
207 | |||
208 | // with input binding as pattern-specific parameters: not declared in interface | ||
209 | |||
210 | /** | ||
211 | * Executes the given processor on each match of the pattern that conforms to the given fixed values of some | ||
212 | * parameters. | ||
213 | * | ||
214 | * @param parameters | ||
215 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
216 | * @pre size of input array must be equal to the number of parameters. | ||
217 | * @param action | ||
218 | * the action that will process each pattern match. | ||
219 | * @since 2.0 | ||
220 | */ | ||
221 | protected void rawForEachMatch(Object[] parameters, Consumer<? super Match> processor) { | ||
222 | backend.getAllMatches(parameters).map(this::tupleToMatch).forEach(processor); | ||
223 | } | ||
224 | |||
225 | @Override | ||
226 | public void forEachMatch(Consumer<? super Match> processor) { | ||
227 | rawForEachMatch(emptyArray(), processor); | ||
228 | } | ||
229 | |||
230 | @Override | ||
231 | public void forEachMatch(Match match, Consumer<? super Match> processor) { | ||
232 | rawForEachMatch(match.toArray(), processor); | ||
233 | } | ||
234 | |||
235 | // with input binding as pattern-specific parameters: not declared in interface | ||
236 | |||
237 | @Override | ||
238 | public boolean forOneArbitraryMatch(Consumer<? super Match> processor) { | ||
239 | return rawForOneArbitraryMatch(emptyArray(), processor); | ||
240 | } | ||
241 | |||
242 | @Override | ||
243 | public boolean forOneArbitraryMatch(Match partialMatch, Consumer<? super Match> processor) { | ||
244 | return rawForOneArbitraryMatch(partialMatch.toArray(), processor); | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * Executes the given processor on an arbitrarily chosen match of the pattern that conforms to the given fixed | ||
249 | * values of some parameters. Neither determinism nor randomness of selection is guaranteed. | ||
250 | * | ||
251 | * @param parameters | ||
252 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
253 | * @pre size of input array must be equal to the number of parameters. | ||
254 | * @param processor | ||
255 | * the action that will process the selected match. | ||
256 | * @return true if the pattern has at least one match with the given parameter values, false if the processor was | ||
257 | * not invoked | ||
258 | * @since 2.0 | ||
259 | */ | ||
260 | protected boolean rawForOneArbitraryMatch(Object[] parameters, Consumer<? super Match> processor) { | ||
261 | return backend.getOneArbitraryMatch(parameters).map(this::tupleToMatch).map(m -> { | ||
262 | processor.accept(m); | ||
263 | return true; | ||
264 | }).orElse(false); | ||
265 | } | ||
266 | |||
267 | // with input binding as pattern-specific parameters: not declared in interface | ||
268 | |||
269 | |||
270 | @Override | ||
271 | public Match newEmptyMatch() { | ||
272 | return arrayToMatchMutable(new Object[getParameterNames().size()]); | ||
273 | } | ||
274 | |||
275 | @Override | ||
276 | public Match newMatch(Object... parameters) { | ||
277 | return arrayToMatch(parameters); | ||
278 | } | ||
279 | |||
280 | @Override | ||
281 | public Set<Object> getAllValues(final String parameterName) { | ||
282 | return rawStreamAllValues(getPositionOfParameter(parameterName), emptyArray()).collect(Collectors.toSet()); | ||
283 | } | ||
284 | |||
285 | @Override | ||
286 | public Set<Object> getAllValues(final String parameterName, Match partialMatch) { | ||
287 | return rawStreamAllValues(getPositionOfParameter(parameterName), partialMatch.toArray()).collect(Collectors.toSet()); | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * Retrieve a stream of values that occur in matches for the given parameterName, that conforms to the given fixed | ||
292 | * values of some parameters. | ||
293 | * | ||
294 | * @param position | ||
295 | * position of the parameter for which values are returned | ||
296 | * @param parameters | ||
297 | * a parameter array corresponding to a partial match of the pattern where each non-null field binds the | ||
298 | * corresponding pattern parameter to a fixed value. | ||
299 | * @return the stream of all values in the given position | ||
300 | * @throws IllegalArgumentException | ||
301 | * if length of parameters array does not equal to number of parameters | ||
302 | * @throws IndexOutOfBoundsException | ||
303 | * if position is not appropriate for the current parameter size | ||
304 | * @since 2.0 | ||
305 | */ | ||
306 | protected Stream<Object> rawStreamAllValues(final int position, Object[] parameters) { | ||
307 | Preconditions.checkElementIndex(position, getParameterNames().size()); | ||
308 | Preconditions.checkArgument(parameters.length == getParameterNames().size()); | ||
309 | return rawStreamAllMatches(parameters).map(match -> match.get(position)); | ||
310 | } | ||
311 | |||
312 | /** | ||
313 | * Uses an existing set to accumulate all values of the parameter with the given name. Since it is a protected | ||
314 | * method, no error checking or input validation is performed! | ||
315 | * | ||
316 | * @param position | ||
317 | * position of the parameter for which values are returned | ||
318 | * @param parameters | ||
319 | * a parameter array corresponding to a partial match of the pattern where each non-null field binds the | ||
320 | * corresponding pattern parameter to a fixed value. | ||
321 | * @param accumulator | ||
322 | * the existing set to fill with the values | ||
323 | */ | ||
324 | @SuppressWarnings("unchecked") | ||
325 | protected <T> void rawAccumulateAllValues(final int position, Object[] parameters, final Set<T> accumulator) { | ||
326 | rawForEachMatch(parameters, match -> accumulator.add((T) match.get(position))); | ||
327 | } | ||
328 | |||
329 | @Override | ||
330 | public ViatraQueryEngine getEngine() { | ||
331 | return engine; | ||
332 | } | ||
333 | |||
334 | @Override | ||
335 | public IQuerySpecification<? extends BaseMatcher<Match>> getSpecification() { | ||
336 | return querySpecification; | ||
337 | } | ||
338 | |||
339 | @Override | ||
340 | public String getPatternName() { | ||
341 | return querySpecification.getFullyQualifiedName(); | ||
342 | } | ||
343 | |||
344 | /** | ||
345 | * @since 1.4 | ||
346 | */ | ||
347 | public IMatcherCapability getCapabilities() { | ||
348 | return capabilities; | ||
349 | } | ||
350 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BasePatternMatch.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BasePatternMatch.java new file mode 100644 index 00000000..182bb466 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BasePatternMatch.java | |||
@@ -0,0 +1,91 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api.impl; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
13 | |||
14 | import java.util.Arrays; | ||
15 | import java.util.Collections; | ||
16 | import java.util.List; | ||
17 | |||
18 | /** | ||
19 | * Base implementation of IPatternMatch. | ||
20 | * | ||
21 | * @author Bergmann Gábor | ||
22 | * | ||
23 | */ | ||
24 | public abstract class BasePatternMatch implements IPatternMatch { | ||
25 | |||
26 | @SafeVarargs | ||
27 | protected static <T> List<T> makeImmutableList(T... elements) { | ||
28 | return Collections.unmodifiableList(Arrays.asList(elements)); | ||
29 | } | ||
30 | |||
31 | public static String prettyPrintValue(Object o) { | ||
32 | if (o == null) { | ||
33 | return "(null)"; | ||
34 | } | ||
35 | return o.toString(); | ||
36 | } | ||
37 | |||
38 | // TODO performance can be improved here somewhat | ||
39 | |||
40 | @Override | ||
41 | public Object get(int position) { | ||
42 | if (position >= 0 && position < parameterNames().size()) | ||
43 | return get(parameterNames().get(position)); | ||
44 | else | ||
45 | return null; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public boolean set(int position, Object newValue) { | ||
50 | if (!isMutable()) throw new UnsupportedOperationException(); | ||
51 | if (position >= 0 && position < parameterNames().size()) { | ||
52 | return set(parameterNames().get(position), newValue); | ||
53 | } else { | ||
54 | return false; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public String toString() { | ||
60 | return "Match<" + patternName() + ">{" + prettyPrint() + "}"; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public boolean isCompatibleWith(IPatternMatch other) { | ||
65 | if(other == null) { | ||
66 | return true; | ||
67 | } | ||
68 | // we assume that the pattern is set for this match! | ||
69 | if (!specification().equals(other.specification())) { | ||
70 | return false; | ||
71 | } | ||
72 | for (int i = 0; i < parameterNames().size(); i++) { | ||
73 | Object value = get(i); | ||
74 | Object otherValue = other.get(i); | ||
75 | if(value != null && otherValue != null && !value.equals(otherValue)) { | ||
76 | return false; | ||
77 | } | ||
78 | } | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public String patternName() { | ||
84 | return specification().getFullyQualifiedName(); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public List<String> parameterNames() { | ||
89 | return specification().getParameterNames(); | ||
90 | } | ||
91 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQueryGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQueryGroup.java new file mode 100644 index 00000000..b92727e7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQueryGroup.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Mark Czotter, Istvan Rath and Daniel Varro | ||
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.api.impl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | ||
12 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
13 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
14 | |||
15 | /** | ||
16 | * Base implementation of {@link IQueryGroup}. | ||
17 | * | ||
18 | * @author Mark Czotter | ||
19 | * | ||
20 | */ | ||
21 | public abstract class BaseQueryGroup implements IQueryGroup { | ||
22 | |||
23 | @Override | ||
24 | public void prepare(ViatraQueryEngine engine) { | ||
25 | prepare(AdvancedViatraQueryEngine.from(engine)); | ||
26 | } | ||
27 | |||
28 | protected void prepare(AdvancedViatraQueryEngine engine) { | ||
29 | engine.prepareGroup(this, null /* default options */); | ||
30 | } | ||
31 | |||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQuerySpecification.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQuerySpecification.java new file mode 100644 index 00000000..bee4b93d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQuerySpecification.java | |||
@@ -0,0 +1,147 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.api.impl; | ||
11 | |||
12 | import java.util.List; | ||
13 | import java.util.Optional; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
17 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
18 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
19 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
20 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility; | ||
27 | |||
28 | /** | ||
29 | * Base implementation of IQuerySpecification. | ||
30 | * | ||
31 | * @author Gabor Bergmann | ||
32 | * | ||
33 | */ | ||
34 | public abstract class BaseQuerySpecification<Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> implements | ||
35 | IQuerySpecification<Matcher> { | ||
36 | |||
37 | /** | ||
38 | * @since 1.6 | ||
39 | */ | ||
40 | protected static ViatraQueryException processInitializerError(ExceptionInInitializerError err) { | ||
41 | Throwable cause1 = err.getCause(); | ||
42 | if (cause1 instanceof RuntimeException) { | ||
43 | Throwable cause2 = ((RuntimeException) cause1).getCause(); | ||
44 | if (cause2 instanceof ViatraQueryException) { | ||
45 | return (ViatraQueryException) cause2; | ||
46 | } else if (cause2 instanceof QueryInitializationException) { | ||
47 | return new ViatraQueryException((QueryInitializationException) cause2); | ||
48 | } | ||
49 | } | ||
50 | throw err; | ||
51 | } | ||
52 | protected final PQuery wrappedPQuery; | ||
53 | |||
54 | protected abstract Matcher instantiate(ViatraQueryEngine engine); | ||
55 | |||
56 | /** | ||
57 | * For backward compatibility of code generated with previous versions of viatra query, this method has a default | ||
58 | * implementation returning null, indicating that a matcher can only be created using the old method, which ignores | ||
59 | * the hints provided by the user. | ||
60 | * | ||
61 | * @since 1.4 | ||
62 | */ | ||
63 | @Override | ||
64 | public Matcher instantiate() { | ||
65 | return null; | ||
66 | } | ||
67 | |||
68 | |||
69 | /** | ||
70 | * Instantiates query specification for the given internal query representation. | ||
71 | */ | ||
72 | public BaseQuerySpecification(PQuery wrappedPQuery) { | ||
73 | super(); | ||
74 | this.wrappedPQuery = wrappedPQuery; | ||
75 | wrappedPQuery.publishedAs().add(this); | ||
76 | } | ||
77 | |||
78 | |||
79 | @Override | ||
80 | public PQuery getInternalQueryRepresentation() { | ||
81 | return wrappedPQuery; | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public Matcher getMatcher(ViatraQueryEngine engine) { | ||
86 | ensureInitializedInternal(); | ||
87 | if (wrappedPQuery.getStatus() == PQueryStatus.ERROR) { | ||
88 | String errorMessages = wrappedPQuery.getPProblems().stream() | ||
89 | .map(input -> (input == null) ? "" : input.getShortMessage()).collect(Collectors.joining("\n")); | ||
90 | throw new ViatraQueryException(String.format("Erroneous query specification: %s %n %s", getFullyQualifiedName(), errorMessages), | ||
91 | "Cannot initialize matchers on erroneous query specifications."); | ||
92 | } else if (!engine.getScope().isCompatibleWithQueryScope(this.getPreferredScopeClass())) { | ||
93 | throw new ViatraQueryException( | ||
94 | String.format( | ||
95 | "Scope class incompatibility: the query %s is formulated over query scopes of class %s, " | ||
96 | + " thus the query engine formulated over scope %s of class %s cannot evaluate it.", | ||
97 | this.getFullyQualifiedName(), this.getPreferredScopeClass().getCanonicalName(), | ||
98 | engine.getScope(), engine.getScope().getClass().getCanonicalName()), | ||
99 | "Incompatible scope classes of engine and query."); | ||
100 | } | ||
101 | return instantiate(engine); | ||
102 | } | ||
103 | |||
104 | protected void ensureInitializedInternal() { | ||
105 | wrappedPQuery.ensureInitialized(); | ||
106 | } | ||
107 | |||
108 | // // DELEGATIONS | ||
109 | |||
110 | @Override | ||
111 | public List<PAnnotation> getAllAnnotations() { | ||
112 | return wrappedPQuery.getAllAnnotations(); | ||
113 | } | ||
114 | @Override | ||
115 | public List<PAnnotation> getAnnotationsByName(String annotationName) { | ||
116 | return wrappedPQuery.getAnnotationsByName(annotationName); | ||
117 | } | ||
118 | @Override | ||
119 | public Optional<PAnnotation> getFirstAnnotationByName(String annotationName) { | ||
120 | return wrappedPQuery.getFirstAnnotationByName(annotationName); | ||
121 | } | ||
122 | @Override | ||
123 | public String getFullyQualifiedName() { | ||
124 | return wrappedPQuery.getFullyQualifiedName(); | ||
125 | } | ||
126 | @Override | ||
127 | public List<String> getParameterNames() { | ||
128 | return wrappedPQuery.getParameterNames(); | ||
129 | } | ||
130 | @Override | ||
131 | public List<PParameter> getParameters() { | ||
132 | return wrappedPQuery.getParameters(); | ||
133 | } | ||
134 | @Override | ||
135 | public Integer getPositionOfParameter(String parameterName) { | ||
136 | return wrappedPQuery.getPositionOfParameter(parameterName); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * @since 2.0 | ||
141 | */ | ||
142 | @Override | ||
143 | public PVisibility getVisibility() { | ||
144 | return wrappedPQuery.getVisibility(); | ||
145 | } | ||
146 | |||
147 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IBaseIndex.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IBaseIndex.java new file mode 100644 index 00000000..1795a8ef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IBaseIndex.java | |||
@@ -0,0 +1,91 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.concurrent.Callable; | ||
13 | |||
14 | /** | ||
15 | * Represents the index maintained on the model. | ||
16 | * @author Bergmann Gabor | ||
17 | * @since 0.9 | ||
18 | * | ||
19 | */ | ||
20 | public interface IBaseIndex { | ||
21 | // TODO lightweightObserver? | ||
22 | // TODO ViatraBaseIndexChangeListener? | ||
23 | |||
24 | /** | ||
25 | * The given callback will be executed, and all model traversals and index registrations will be delayed until the | ||
26 | * execution is done. If there are any outstanding feature, class or datatype registrations, a single coalesced model | ||
27 | * traversal will initialize the caches and deliver the notifications. | ||
28 | * | ||
29 | * @param callable | ||
30 | */ | ||
31 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; | ||
32 | |||
33 | /** | ||
34 | * Adds a coarse-grained listener that will be invoked after the NavigationHelper index or the underlying model is changed. Can be used | ||
35 | * e.g. to check model contents. Not intended for general use. | ||
36 | * | ||
37 | * <p/> See {@link #removeBaseIndexChangeListener(ViatraBaseIndexChangeListener)} | ||
38 | * @param listener | ||
39 | */ | ||
40 | public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener); | ||
41 | |||
42 | /** | ||
43 | * Removes a registered listener. | ||
44 | * | ||
45 | * <p/> See {@link #addBaseIndexChangeListener(ViatraBaseIndexChangeListener)} | ||
46 | * | ||
47 | * @param listener | ||
48 | */ | ||
49 | public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener); | ||
50 | |||
51 | /** | ||
52 | * Updates the value of indexed derived features that are not well-behaving. | ||
53 | */ | ||
54 | void resampleDerivedFeatures(); | ||
55 | |||
56 | /** | ||
57 | * Adds a listener for internal errors in the index. A listener can only be added once. | ||
58 | * @param listener | ||
59 | * @returns true if the listener was not already added | ||
60 | * @since 0.8.0 | ||
61 | */ | ||
62 | boolean addIndexingErrorListener(IIndexingErrorListener listener); | ||
63 | /** | ||
64 | * Removes a listener for internal errors in the index | ||
65 | * @param listener | ||
66 | * @returns true if the listener was successfully removed (e.g. it did exist) | ||
67 | * @since 0.8.0 | ||
68 | */ | ||
69 | boolean removeIndexingErrorListener(IIndexingErrorListener listener); | ||
70 | |||
71 | /** | ||
72 | * Register a lightweight observer that is notified if any edge starting at the given Object changes. | ||
73 | * | ||
74 | * @param observer the listener instance | ||
75 | * @param observedObject the observed instance object | ||
76 | * @return false if no observer can be registered for the given instance (e.g. it is a primitive), | ||
77 | * or observer was already registered (call has no effect) | ||
78 | */ | ||
79 | public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject); | ||
80 | |||
81 | /** | ||
82 | * Unregisters a lightweight observer for the given Object. | ||
83 | * | ||
84 | * @param observer the listener instance | ||
85 | * @param observedObject the observed instance object | ||
86 | * @return false if no observer can be registered for the given instance (e.g. it is a primitive), | ||
87 | * or no observer was registered previously (call has no effect) | ||
88 | */ | ||
89 | public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject); | ||
90 | |||
91 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IEngineContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IEngineContext.java new file mode 100644 index 00000000..55060853 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IEngineContext.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
13 | |||
14 | /** | ||
15 | * The context of the engine is instantiated by the scope, | ||
16 | * and provides information and services regarding the model the towards the engine. | ||
17 | * | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public interface IEngineContext { | ||
22 | |||
23 | /** | ||
24 | * Returns the base index. | ||
25 | * @throws ViatraQueryRuntimeException if the base index cannot be accessed | ||
26 | */ | ||
27 | IBaseIndex getBaseIndex(); | ||
28 | |||
29 | /** | ||
30 | * Disposes this context object. Resources in the index may now be freed up. | ||
31 | * No more methods should be called after this one. | ||
32 | * | ||
33 | * @throws IllegalStateException if there are any active listeners to the underlying index | ||
34 | */ | ||
35 | void dispose(); | ||
36 | |||
37 | /** | ||
38 | * Provides instance model information for pattern matching. | ||
39 | * | ||
40 | * <p> Implementors note: must be reentrant. | ||
41 | * If called while index loading is already in progress, must return the single runtime context instance that will eventually index the model. | ||
42 | * When the runtime query context is invoked in such a case, incomplete indexes are tolerable, but change notifications must be correctly provided as loading commences. | ||
43 | * | ||
44 | * @return a runtime context for pattern matching | ||
45 | * @since 1.2 | ||
46 | * @throws ViatraQueryRuntimeException if the runtime context cannot be initialized | ||
47 | */ | ||
48 | public IQueryRuntimeContext getQueryRuntimeContext(); | ||
49 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IIndexingErrorListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IIndexingErrorListener.java new file mode 100644 index 00000000..f61a5edb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IIndexingErrorListener.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | /** | ||
12 | * | ||
13 | * This interface contains callbacks for various internal errors from the {@link NavigationHelper base index}. | ||
14 | * | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * @since 0.9 | ||
17 | * | ||
18 | */ | ||
19 | public interface IIndexingErrorListener { | ||
20 | |||
21 | void error(String description, Throwable t); | ||
22 | void fatal(String description, Throwable t); | ||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IInstanceObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IInstanceObserver.java new file mode 100644 index 00000000..8ef29cbe --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IInstanceObserver.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | |||
12 | /** | ||
13 | * Listener interface for lightweight observation of changes in edges leaving from given source instance elements. | ||
14 | * @author Bergmann Gabor | ||
15 | * @since 0.9 | ||
16 | * | ||
17 | */ | ||
18 | public interface IInstanceObserver { | ||
19 | void notifyBinaryChanged(Object sourceElement, Object edgeType); | ||
20 | void notifyTernaryChanged(Object sourceElement, Object edgeType); | ||
21 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/QueryScope.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/QueryScope.java new file mode 100644 index 00000000..5456b9ea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/QueryScope.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
12 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
13 | import tools.refinery.viatra.runtime.internal.apiimpl.EngineContextFactory; | ||
14 | |||
15 | /** | ||
16 | * Defines a scope for a VIATRA Query engine, which determines the set of model elements that query evaluation operates on. | ||
17 | * | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public abstract class QueryScope extends EngineContextFactory { | ||
22 | |||
23 | /** | ||
24 | * Determines whether a query engine initialized on this scope can evaluate queries formulated against the given scope type. | ||
25 | * <p> Every query scope class is compatible with a query engine initialized on a scope of the same class or a subclass. | ||
26 | * @param queryScopeClass the scope class returned by invoking {@link IQuerySpecification#getPreferredScopeClass()} on a query specification | ||
27 | * @return true if an {@link ViatraQueryEngine} initialized on this scope can consume an {@link IQuerySpecification} | ||
28 | */ | ||
29 | public boolean isCompatibleWithQueryScope(Class<? extends QueryScope> queryScopeClass) { | ||
30 | return queryScopeClass.isAssignableFrom(this.getClass()); | ||
31 | } | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/ViatraBaseIndexChangeListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/ViatraBaseIndexChangeListener.java new file mode 100644 index 00000000..b746e637 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/ViatraBaseIndexChangeListener.java | |||
@@ -0,0 +1,34 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.api.scope; | ||
10 | |||
11 | /** | ||
12 | * Listener interface for change notifications from the VIATRA Base index. | ||
13 | * | ||
14 | * @author Abel Hegedus | ||
15 | * @since 0.9 | ||
16 | * | ||
17 | */ | ||
18 | public interface ViatraBaseIndexChangeListener { | ||
19 | |||
20 | /** | ||
21 | * NOTE: it is possible that this method is called only ONCE! Consider returning a constant value that is set in the constructor. | ||
22 | * | ||
23 | * @return true, if the listener should be notified only after index changes, false if notification is needed after each model change | ||
24 | */ | ||
25 | public boolean onlyOnIndexChange(); | ||
26 | |||
27 | /** | ||
28 | * Called after a model change is handled by the VIATRA Base index and if <code>indexChanged == onlyOnIndexChange()</code>. | ||
29 | * | ||
30 | * @param indexChanged true, if the model change also affected the contents of the base index | ||
31 | */ | ||
32 | public void notifyChanged(boolean indexChanged); | ||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/exception/ViatraQueryException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/exception/ViatraQueryException.java new file mode 100644 index 00000000..fec547a6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/exception/ViatraQueryException.java | |||
@@ -0,0 +1,71 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Akos Horvath and Daniel Varro | ||
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.exception; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; | ||
14 | |||
15 | /** | ||
16 | * A general VIATRA Query-related problem during the operation of the VIATRA Query | ||
17 | * engine, or the loading, manipulation and evaluation of queries. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * @since 0.9 | ||
21 | * | ||
22 | */ | ||
23 | public class ViatraQueryException extends ViatraQueryRuntimeException { | ||
24 | |||
25 | private static final long serialVersionUID = -74252748358355750L; | ||
26 | |||
27 | public static final String PARAM_NOT_SUITABLE_WITH_NO = "The type of the parameters are not suitable for the operation. Parameter number: "; | ||
28 | public static final String CONVERSION_FAILED = "Could not convert the term to the designated type"; | ||
29 | public static final String CONVERT_NULL_PARAMETER = "Could not convert null to the designated type"; | ||
30 | public static final String RELATIONAL_PARAM_UNSUITABLE = "The parameters are not acceptable by the operation"; | ||
31 | /** | ||
32 | * @since 0.9 | ||
33 | */ | ||
34 | public static final String PROCESSING_PROBLEM = "The following error occurred during the processing of a query (e.g. for the preparation of a VIATRA pattern matcher)"; | ||
35 | /** | ||
36 | * @since 0.9 | ||
37 | */ | ||
38 | public static final String QUERY_INIT_PROBLEM = "The following error occurred during the initialization of a VIATRA query specification"; | ||
39 | public static final String GETNAME_FAILED = "Could not get 'name' attribute of the result"; | ||
40 | |||
41 | public static final String INVALID_EMFROOT = "Incremental EMF query engine can only be attached on the contents of an EMF EObject, Resource, ResourceSet or multiple ResourceSets. Received instead: "; | ||
42 | public static final String INVALID_EMFROOT_SHORT = "Invalid EMF model root"; | ||
43 | // public static final String EMF_MODEL_PROCESSING_ERROR = "Error while processing the EMF model"; | ||
44 | |||
45 | private final String shortMessage; | ||
46 | |||
47 | public ViatraQueryException(String s, String shortMessage) { | ||
48 | super(s); | ||
49 | this.shortMessage = shortMessage; | ||
50 | } | ||
51 | |||
52 | public ViatraQueryException(QueryProcessingException e) { | ||
53 | super(PROCESSING_PROBLEM + ": " + e.getMessage(), e); | ||
54 | this.shortMessage = e.getShortMessage(); | ||
55 | } | ||
56 | |||
57 | public ViatraQueryException(QueryInitializationException e) { | ||
58 | super(QUERY_INIT_PROBLEM + ": " + e.getMessage(), e); | ||
59 | this.shortMessage = e.getShortMessage(); | ||
60 | } | ||
61 | |||
62 | public ViatraQueryException(String s, String shortMessage, Throwable e) { | ||
63 | super(s + ": " + e.getMessage(), e); | ||
64 | this.shortMessage = shortMessage; | ||
65 | } | ||
66 | |||
67 | public String getShortMessage() { | ||
68 | return shortMessage; | ||
69 | } | ||
70 | |||
71 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/EngineContextFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/EngineContextFactory.java new file mode 100644 index 00000000..bed07ebf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/EngineContextFactory.java | |||
@@ -0,0 +1,24 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.internal.apiimpl; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
13 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
14 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
15 | |||
16 | /** | ||
17 | * Internal interface for a Scope to reveal model contents to the engine. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public abstract class EngineContextFactory { | ||
23 | protected abstract IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, Logger logger); | ||
24 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/QueryResultWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/QueryResultWrapper.java new file mode 100644 index 00000000..47f3268d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/QueryResultWrapper.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.internal.apiimpl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
14 | |||
15 | /** | ||
16 | * Internal class for wrapping a query result providing backend. It's only supported usage is by the | ||
17 | * {@link ViatraQueryEngineImpl} class. | ||
18 | * </p> | ||
19 | * | ||
20 | * <strong>Important note</strong>: this class must not introduce any public method, as it will be visible through | ||
21 | * BaseMatcher as an API, although this class is not an API itself. | ||
22 | * | ||
23 | * @author Bergmann Gabor | ||
24 | * | ||
25 | */ | ||
26 | public abstract class QueryResultWrapper { | ||
27 | |||
28 | protected IQueryResultProvider backend; | ||
29 | |||
30 | protected abstract void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider, IMatcherCapability capabilities); | ||
31 | |||
32 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java new file mode 100644 index 00000000..5317a79e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java | |||
@@ -0,0 +1,714 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | |||
11 | package tools.refinery.viatra.runtime.internal.apiimpl; | ||
12 | |||
13 | import org.apache.log4j.Logger; | ||
14 | import tools.refinery.viatra.runtime.api.*; | ||
15 | import tools.refinery.viatra.runtime.api.impl.BaseMatcher; | ||
16 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
17 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
18 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
19 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
20 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
21 | import tools.refinery.viatra.runtime.internal.engine.LifecycleProvider; | ||
22 | import tools.refinery.viatra.runtime.internal.engine.ModelUpdateProvider; | ||
23 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
24 | import tools.refinery.viatra.runtime.matchers.backend.*; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
28 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueries; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
34 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
35 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
36 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
37 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
38 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
39 | |||
40 | import java.lang.ref.WeakReference; | ||
41 | import java.lang.reflect.InvocationTargetException; | ||
42 | import java.util.*; | ||
43 | import java.util.concurrent.Callable; | ||
44 | import java.util.function.Supplier; | ||
45 | import java.util.stream.Collectors; | ||
46 | |||
47 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
48 | |||
49 | /** | ||
50 | * A VIATRA Query engine back-end (implementation) | ||
51 | * | ||
52 | * @author Bergmann Gábor | ||
53 | */ | ||
54 | public final class ViatraQueryEngineImpl extends AdvancedViatraQueryEngine | ||
55 | implements IQueryBackendHintProvider, IQueryCacheContext, IQueryResultProviderAccess { | ||
56 | |||
57 | /** | ||
58 | * | ||
59 | */ | ||
60 | private static final String ERROR_ACCESSING_BACKEND = "Error while accessing query evaluator backend"; | ||
61 | /** | ||
62 | * | ||
63 | */ | ||
64 | private static final String QUERY_ON_DISPOSED_ENGINE_MESSAGE = "Cannot evaluate query on disposed engine!"; | ||
65 | /** | ||
66 | * The engine manager responsible for this engine. Null if this engine is unmanaged. | ||
67 | */ | ||
68 | private final ViatraQueryEngineManager manager; | ||
69 | /** | ||
70 | * The model to which the engine is attached. | ||
71 | */ | ||
72 | private final QueryScope scope; | ||
73 | |||
74 | /** | ||
75 | * The context of the engine, provided by the scope. | ||
76 | */ | ||
77 | private IEngineContext engineContext; | ||
78 | |||
79 | /** | ||
80 | * Initialized matchers for each query | ||
81 | */ | ||
82 | private final IMultiLookup<IQuerySpecification<? extends ViatraQueryMatcher<?>>, ViatraQueryMatcher<?>> matchers = | ||
83 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
84 | |||
85 | /** | ||
86 | * The RETE and other pattern matcher implementations of the VIATRA Query Engine. | ||
87 | */ | ||
88 | private volatile Map<IQueryBackendFactory, IQueryBackend> queryBackends = new HashMap<>(); | ||
89 | |||
90 | /** | ||
91 | * The current engine default hints | ||
92 | */ | ||
93 | private final ViatraQueryEngineOptions engineOptions; | ||
94 | |||
95 | /** | ||
96 | * Common query analysis provided to backends | ||
97 | */ | ||
98 | private QueryAnalyzer queryAnalyzer; | ||
99 | |||
100 | /** | ||
101 | * true if message delivery is currently delayed, false otherwise | ||
102 | */ | ||
103 | private boolean delayMessageDelivery = true; | ||
104 | |||
105 | private final LifecycleProvider lifecycleProvider; | ||
106 | private final ModelUpdateProvider modelUpdateProvider; | ||
107 | private Logger logger; | ||
108 | private boolean disposed = false; | ||
109 | |||
110 | /** | ||
111 | * @param manager | ||
112 | * null if unmanaged | ||
113 | * @param scope | ||
114 | * @param engineDefaultHint | ||
115 | * @since 1.4 | ||
116 | */ | ||
117 | public ViatraQueryEngineImpl(ViatraQueryEngineManager manager, QueryScope scope, | ||
118 | ViatraQueryEngineOptions engineOptions) { | ||
119 | super(); | ||
120 | this.manager = manager; | ||
121 | this.scope = scope; | ||
122 | this.lifecycleProvider = new LifecycleProvider(this, getLogger()); | ||
123 | this.modelUpdateProvider = new ModelUpdateProvider(this, getLogger()); | ||
124 | this.engineContext = scope.createEngineContext(this, taintListener, getLogger()); | ||
125 | |||
126 | if (engineOptions != null) { | ||
127 | this.engineOptions = engineOptions; | ||
128 | } else { | ||
129 | this.engineOptions = ViatraQueryEngineOptions.getDefault(); | ||
130 | } | ||
131 | |||
132 | } | ||
133 | |||
134 | /** | ||
135 | * @param manager | ||
136 | * null if unmanaged | ||
137 | * @param scope | ||
138 | * @param engineDefaultHint | ||
139 | */ | ||
140 | public ViatraQueryEngineImpl(ViatraQueryEngineManager manager, QueryScope scope) { | ||
141 | this(manager, scope, ViatraQueryEngineOptions.getDefault()); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public boolean isUpdatePropagationDelayed() { | ||
146 | return this.delayMessageDelivery; | ||
147 | } | ||
148 | |||
149 | @Override | ||
150 | public <V> V delayUpdatePropagation(Callable<V> callable) throws InvocationTargetException { | ||
151 | if (!delayMessageDelivery) { | ||
152 | throw new IllegalStateException("Trying to delay propagation while changes are being flushed"); | ||
153 | } | ||
154 | try { | ||
155 | return callable.call(); | ||
156 | } catch (Exception e) { | ||
157 | throw new InvocationTargetException(e); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public void flushChanges() { | ||
163 | if (!delayMessageDelivery) { | ||
164 | throw new IllegalStateException("Trying to flush changes while changes are already being flushed"); | ||
165 | } | ||
166 | delayMessageDelivery = false; | ||
167 | try { | ||
168 | flushAllBackends(); | ||
169 | } finally { | ||
170 | delayMessageDelivery = true; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | private void flushAllBackends() { | ||
175 | for (IQueryBackend backend : this.queryBackends.values()) { | ||
176 | backend.flushUpdates(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | @Override | ||
181 | public <T> T withFlushingChanges(Supplier<T> callback) { | ||
182 | if (!delayMessageDelivery) { | ||
183 | return callback.get(); | ||
184 | } | ||
185 | delayMessageDelivery = false; | ||
186 | try { | ||
187 | flushAllBackends(); | ||
188 | return callback.get(); | ||
189 | } finally { | ||
190 | delayMessageDelivery = true; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | @Override | ||
195 | public Set<? extends ViatraQueryMatcher<? extends IPatternMatch>> getCurrentMatchers() { | ||
196 | return matchers.distinctValuesStream().collect(Collectors.toSet()); | ||
197 | } | ||
198 | |||
199 | @Override | ||
200 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
201 | IQuerySpecification<Matcher> querySpecification) { | ||
202 | return getMatcher(querySpecification, null); | ||
203 | } | ||
204 | |||
205 | @Override | ||
206 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
207 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalEvaluationHints) { | ||
208 | return withFlushingChanges(() -> { | ||
209 | IMatcherCapability capability = getRequestedCapability(querySpecification, optionalEvaluationHints); | ||
210 | Matcher matcher = doGetExistingMatcher(querySpecification, capability); | ||
211 | if (matcher != null) { | ||
212 | return matcher; | ||
213 | } | ||
214 | matcher = querySpecification.instantiate(); | ||
215 | |||
216 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; | ||
217 | ((QueryResultWrapper) baseMatcher).setBackend(this, | ||
218 | getResultProvider(querySpecification, optionalEvaluationHints), capability); | ||
219 | internalRegisterMatcher(querySpecification, baseMatcher); | ||
220 | return matcher; | ||
221 | }); | ||
222 | } | ||
223 | |||
224 | @Override | ||
225 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher( | ||
226 | IQuerySpecification<Matcher> querySpecification) { | ||
227 | return getExistingMatcher(querySpecification, null); | ||
228 | } | ||
229 | |||
230 | @Override | ||
231 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher( | ||
232 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalOverrideHints) { | ||
233 | return doGetExistingMatcher(querySpecification, getRequestedCapability(querySpecification, optionalOverrideHints)); | ||
234 | } | ||
235 | |||
236 | @SuppressWarnings("unchecked") | ||
237 | private <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher doGetExistingMatcher( | ||
238 | IQuerySpecification<Matcher> querySpecification, IMatcherCapability requestedCapability) { | ||
239 | for (ViatraQueryMatcher<?> matcher : matchers.lookupOrEmpty(querySpecification)) { | ||
240 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; | ||
241 | if (baseMatcher.getCapabilities().canBeSubstitute(requestedCapability)) | ||
242 | return (Matcher) matcher; | ||
243 | } | ||
244 | return null; | ||
245 | } | ||
246 | |||
247 | @Override | ||
248 | public ViatraQueryMatcher<? extends IPatternMatch> getMatcher(String patternFQN) { | ||
249 | throw new UnsupportedOperationException("Query specification registry is not available"); | ||
250 | } | ||
251 | |||
252 | @Override | ||
253 | public IBaseIndex getBaseIndex() { | ||
254 | return engineContext.getBaseIndex(); | ||
255 | } | ||
256 | |||
257 | public final Logger getLogger() { | ||
258 | if (logger == null) { | ||
259 | final int hash = System.identityHashCode(this); | ||
260 | logger = Logger.getLogger(ViatraQueryLoggingUtil.getLogger(ViatraQueryEngine.class).getName() + "." + hash); | ||
261 | if (logger == null) | ||
262 | throw new AssertionError( | ||
263 | "Configuration error: unable to create VIATRA Query runtime logger for engine " + hash); | ||
264 | } | ||
265 | return logger; | ||
266 | } | ||
267 | |||
268 | ///////////////// internal stuff ////////////// | ||
269 | private void internalRegisterMatcher(IQuerySpecification<?> querySpecification, ViatraQueryMatcher<?> matcher) { | ||
270 | matchers.addPair(querySpecification, matcher); | ||
271 | lifecycleProvider.matcherInstantiated(matcher); | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * Provides access to the selected query backend component of the VIATRA Query Engine. | ||
276 | */ | ||
277 | @Override | ||
278 | public IQueryBackend getQueryBackend(IQueryBackendFactory iQueryBackendFactory) { | ||
279 | IQueryBackend iQueryBackend = queryBackends.get(iQueryBackendFactory); | ||
280 | if (iQueryBackend == null) { | ||
281 | // do this first, to make sure the runtime context exists | ||
282 | final IQueryRuntimeContext queryRuntimeContext = engineContext.getQueryRuntimeContext(); | ||
283 | |||
284 | // maybe the backend has been created in the meantime when the indexer was initialized and queried for | ||
285 | // derived features | ||
286 | // no need to instantiate a new backend in that case | ||
287 | iQueryBackend = queryBackends.get(iQueryBackendFactory); | ||
288 | if (iQueryBackend == null) { | ||
289 | |||
290 | // need to instantiate the backend | ||
291 | iQueryBackend = iQueryBackendFactory.create(new IQueryBackendContext() { | ||
292 | |||
293 | @Override | ||
294 | public IQueryRuntimeContext getRuntimeContext() { | ||
295 | return queryRuntimeContext; | ||
296 | } | ||
297 | |||
298 | @Override | ||
299 | public IQueryCacheContext getQueryCacheContext() { | ||
300 | return ViatraQueryEngineImpl.this; | ||
301 | } | ||
302 | |||
303 | @Override | ||
304 | public Logger getLogger() { | ||
305 | return logger; | ||
306 | } | ||
307 | |||
308 | @Override | ||
309 | public IQueryBackendHintProvider getHintProvider() { | ||
310 | return ViatraQueryEngineImpl.this; | ||
311 | } | ||
312 | |||
313 | @Override | ||
314 | public IQueryResultProviderAccess getResultProviderAccess() { | ||
315 | return ViatraQueryEngineImpl.this; | ||
316 | } | ||
317 | |||
318 | @Override | ||
319 | public QueryAnalyzer getQueryAnalyzer() { | ||
320 | if (queryAnalyzer == null) | ||
321 | queryAnalyzer = new QueryAnalyzer(queryRuntimeContext.getMetaContext()); | ||
322 | return queryAnalyzer; | ||
323 | } | ||
324 | |||
325 | @Override | ||
326 | public boolean areUpdatesDelayed() { | ||
327 | return ViatraQueryEngineImpl.this.delayMessageDelivery; | ||
328 | } | ||
329 | |||
330 | @Override | ||
331 | public IMatcherCapability getRequiredMatcherCapability(PQuery query, | ||
332 | QueryEvaluationHint hint) { | ||
333 | return engineOptions.getQueryBackendFactory(hint).calculateRequiredCapability(query, hint); | ||
334 | } | ||
335 | |||
336 | |||
337 | |||
338 | }); | ||
339 | queryBackends.put(iQueryBackendFactory, iQueryBackend); | ||
340 | } | ||
341 | } | ||
342 | return iQueryBackend; | ||
343 | } | ||
344 | |||
345 | ///////////////// advanced stuff ///////////// | ||
346 | |||
347 | @Override | ||
348 | public void dispose() { | ||
349 | if (manager != null) { | ||
350 | throw new UnsupportedOperationException( | ||
351 | String.format("Cannot dispose() managed VIATRA Query Engine. Attempted for scope %s.", scope)); | ||
352 | } | ||
353 | wipe(); | ||
354 | |||
355 | this.disposed = true; | ||
356 | |||
357 | // called before base index disposal to allow removal of base listeners | ||
358 | lifecycleProvider.engineDisposed(); | ||
359 | |||
360 | try { | ||
361 | engineContext.dispose(); | ||
362 | } catch (IllegalStateException ex) { | ||
363 | getLogger().warn( | ||
364 | "The base index could not be disposed along with the VIATRA Query engine, as there are still active listeners on it."); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | @Override | ||
369 | public void wipe() { | ||
370 | if (manager != null) { | ||
371 | throw new UnsupportedOperationException( | ||
372 | String.format("Cannot wipe() managed VIATRA Query Engine. Attempted for scope %s.", scope)); | ||
373 | } | ||
374 | if (queryBackends != null) { | ||
375 | for (IQueryBackend backend : queryBackends.values()) { | ||
376 | backend.dispose(); | ||
377 | } | ||
378 | queryBackends.clear(); | ||
379 | } | ||
380 | matchers.clear(); | ||
381 | queryAnalyzer = null; | ||
382 | lifecycleProvider.engineWiped(); | ||
383 | } | ||
384 | |||
385 | /** | ||
386 | * Indicates whether the engine is in a tainted, inconsistent state. | ||
387 | */ | ||
388 | private boolean tainted = false; | ||
389 | private IIndexingErrorListener taintListener = new SelfTaintListener(this); | ||
390 | |||
391 | private static class SelfTaintListener implements IIndexingErrorListener { | ||
392 | WeakReference<ViatraQueryEngineImpl> queryEngineRef; | ||
393 | |||
394 | public SelfTaintListener(ViatraQueryEngineImpl queryEngine) { | ||
395 | this.queryEngineRef = new WeakReference<ViatraQueryEngineImpl>(queryEngine); | ||
396 | } | ||
397 | |||
398 | public void engineBecameTainted(String description, Throwable t) { | ||
399 | final ViatraQueryEngineImpl queryEngine = queryEngineRef.get(); | ||
400 | if (queryEngine != null) { | ||
401 | queryEngine.tainted = true; | ||
402 | queryEngine.lifecycleProvider.engineBecameTainted(description, t); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | private boolean noTaintDetectedYet = true; | ||
407 | |||
408 | protected void notifyTainted(String description, Throwable t) { | ||
409 | if (noTaintDetectedYet) { | ||
410 | noTaintDetectedYet = false; | ||
411 | engineBecameTainted(description, t); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | @Override | ||
416 | public void error(String description, Throwable t) { | ||
417 | // Errors does not mean tainting | ||
418 | } | ||
419 | |||
420 | @Override | ||
421 | public void fatal(String description, Throwable t) { | ||
422 | notifyTainted(description, t); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | @Override | ||
427 | public boolean isTainted() { | ||
428 | return tainted; | ||
429 | } | ||
430 | |||
431 | @Override | ||
432 | public boolean isManaged() { | ||
433 | return manager != null; | ||
434 | // return isAdvanced; ??? | ||
435 | } | ||
436 | |||
437 | private <Match extends IPatternMatch> IQueryResultProvider getUnderlyingResultProvider( | ||
438 | final BaseMatcher<Match> matcher) { | ||
439 | // IQueryResultProvider resultProvider = reteEngine.accessMatcher(matcher.getSpecification()); | ||
440 | return matcher.backend; | ||
441 | } | ||
442 | |||
443 | @Override | ||
444 | public <Match extends IPatternMatch> void addMatchUpdateListener(final ViatraQueryMatcher<Match> matcher, | ||
445 | final IMatchUpdateListener<? super Match> listener, boolean fireNow) { | ||
446 | |||
447 | checkArgument(listener != null, "Cannot add null listener!"); | ||
448 | checkArgument(matcher.getEngine() == this, "Cannot register listener for matcher of different engine!"); | ||
449 | checkArgument(!disposed, "Cannot register listener on matcher of disposed engine!"); | ||
450 | |||
451 | final BaseMatcher<Match> bm = (BaseMatcher<Match>) matcher; | ||
452 | |||
453 | final IUpdateable updateDispatcher = (updateElement, isInsertion) -> { | ||
454 | Match match = null; | ||
455 | try { | ||
456 | match = bm.newMatch(updateElement.getElements()); | ||
457 | if (isInsertion) | ||
458 | listener.notifyAppearance(match); | ||
459 | else | ||
460 | listener.notifyDisappearance(match); | ||
461 | } catch (Throwable e) { // NOPMD | ||
462 | if (e instanceof Error) | ||
463 | throw (Error) e; | ||
464 | logger.warn( | ||
465 | String.format( | ||
466 | "The incremental pattern matcher encountered an error during %s a callback on %s of match %s of pattern %s. Error message: %s. (Developer note: %s in %s callback)", | ||
467 | match == null ? "preparing" : "invoking", isInsertion ? "insertion" : "removal", | ||
468 | match == null ? updateElement.toString() : match.prettyPrint(), | ||
469 | matcher.getPatternName(), e.getMessage(), e.getClass().getSimpleName(), listener), | ||
470 | e); | ||
471 | } | ||
472 | |||
473 | }; | ||
474 | |||
475 | IQueryResultProvider resultProvider = getUnderlyingResultProvider(bm); | ||
476 | resultProvider.addUpdateListener(updateDispatcher, listener, fireNow); | ||
477 | } | ||
478 | |||
479 | @Override | ||
480 | public <Match extends IPatternMatch> void removeMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
481 | IMatchUpdateListener<? super Match> listener) { | ||
482 | checkArgument(listener != null, "Cannot remove null listener!"); | ||
483 | checkArgument(matcher.getEngine() == this, "Cannot remove listener from matcher of different engine!"); | ||
484 | checkArgument(!disposed, "Cannot remove listener from matcher of disposed engine!"); | ||
485 | |||
486 | final BaseMatcher<Match> bm = (BaseMatcher<Match>) matcher; | ||
487 | |||
488 | try { | ||
489 | IQueryResultProvider resultProvider = getUnderlyingResultProvider(bm); | ||
490 | resultProvider.removeUpdateListener(listener); | ||
491 | } catch (Exception e) { | ||
492 | logger.error( | ||
493 | "Error while removing listener " + listener + " from the matcher of " + matcher.getPatternName(), | ||
494 | e); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | @Override | ||
499 | public void addModelUpdateListener(ViatraQueryModelUpdateListener listener) { | ||
500 | modelUpdateProvider.addListener(listener); | ||
501 | } | ||
502 | |||
503 | @Override | ||
504 | public void removeModelUpdateListener(ViatraQueryModelUpdateListener listener) { | ||
505 | modelUpdateProvider.removeListener(listener); | ||
506 | } | ||
507 | |||
508 | @Override | ||
509 | public void addLifecycleListener(ViatraQueryEngineLifecycleListener listener) { | ||
510 | lifecycleProvider.addListener(listener); | ||
511 | } | ||
512 | |||
513 | @Override | ||
514 | public void removeLifecycleListener(ViatraQueryEngineLifecycleListener listener) { | ||
515 | lifecycleProvider.removeListener(listener); | ||
516 | } | ||
517 | |||
518 | /** | ||
519 | * Returns an internal interface towards the query backend to feed the matcher with results. | ||
520 | * | ||
521 | * @param query | ||
522 | * the pattern for which the result provider should be delivered | ||
523 | * | ||
524 | * @throws ViatraQueryRuntimeException | ||
525 | */ | ||
526 | public IQueryResultProvider getResultProvider(IQuerySpecification<?> query) { | ||
527 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
528 | |||
529 | return getResultProviderInternal(query, null); | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * Returns an internal interface towards the query backend to feed the matcher with results. | ||
534 | * | ||
535 | * @param query | ||
536 | * the pattern for which the result provider should be delivered | ||
537 | * | ||
538 | * @throws ViatraQueryRuntimeException | ||
539 | */ | ||
540 | public IQueryResultProvider getResultProvider(IQuerySpecification<?> query, QueryEvaluationHint hint) { | ||
541 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
542 | |||
543 | return getResultProviderInternal(query, hint); | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * This method returns the result provider exactly as described by the passed hint. Query cannot be null! Use | ||
548 | * {@link #getQueryEvaluationHint(IQuerySpecification, QueryEvaluationHint)} before passing a hint to this method to | ||
549 | * make sure engine and query specific hints are correctly applied. | ||
550 | * | ||
551 | * @throws ViatraQueryRuntimeException | ||
552 | */ | ||
553 | private IQueryResultProvider getResultProviderInternal(IQuerySpecification<?> query, QueryEvaluationHint hint) { | ||
554 | return getResultProviderInternal(query.getInternalQueryRepresentation(), hint); | ||
555 | } | ||
556 | |||
557 | /** | ||
558 | * This method returns the result provider exactly as described by the passed hint. Query cannot be null! Use | ||
559 | * {@link #getQueryEvaluationHint(IQuerySpecification, QueryEvaluationHint)} before passing a hint to this method to | ||
560 | * make sure engine and query specific hints are correctly applied. | ||
561 | * | ||
562 | * @throws ViatraQueryRuntimeException | ||
563 | */ | ||
564 | private IQueryResultProvider getResultProviderInternal(PQuery query, QueryEvaluationHint hint) { | ||
565 | Preconditions.checkArgument(query != null, "Query cannot be null!"); | ||
566 | Preconditions.checkArgument(query.getStatus() != PQueryStatus.ERROR, "Cannot initialize a result provider for the erronoues query `%s`.", query.getSimpleName()); | ||
567 | final IQueryBackend backend = getQueryBackend(engineOptions.getQueryBackendFactory(getQueryEvaluationHint(query, hint))); | ||
568 | return backend.getResultProvider(query, hint); | ||
569 | } | ||
570 | |||
571 | /** | ||
572 | * Returns the query backend (influenced by the hint system), even if it is a non-caching backend. | ||
573 | * | ||
574 | * @throws ViatraQueryRuntimeException | ||
575 | */ | ||
576 | private IQueryBackend getQueryBackend(PQuery query) { | ||
577 | final IQueryBackendFactory factory = engineOptions.getQueryBackendFactory(getQueryEvaluationHint(query)); | ||
578 | return getQueryBackend(factory); | ||
579 | } | ||
580 | |||
581 | /** | ||
582 | * Returns a caching query backend (influenced by the hint system). | ||
583 | * | ||
584 | * @throws ViatraQueryRuntimeException | ||
585 | */ | ||
586 | private IQueryBackend getCachingQueryBackend(PQuery query) { | ||
587 | IQueryBackend regularBackend = getQueryBackend(query); | ||
588 | if (regularBackend.isCaching()) | ||
589 | return regularBackend; | ||
590 | else | ||
591 | return getQueryBackend(engineOptions.getDefaultCachingBackendFactory()); | ||
592 | } | ||
593 | |||
594 | @Override | ||
595 | public boolean isResultCached(PQuery query) { | ||
596 | try { | ||
597 | return null != getCachingQueryBackend(query).peekExistingResultProvider(query); | ||
598 | } catch (ViatraQueryException iqe) { | ||
599 | getLogger().error(ERROR_ACCESSING_BACKEND, iqe); | ||
600 | return false; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | @Override | ||
605 | public IQueryResultProvider getCachingResultProvider(PQuery query) { | ||
606 | try { | ||
607 | return getCachingQueryBackend(query).getResultProvider(query); | ||
608 | } catch (ViatraQueryException iqe) { | ||
609 | getLogger().error(ERROR_ACCESSING_BACKEND, iqe); | ||
610 | throw iqe; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | private QueryEvaluationHint getEngineDefaultHint() { | ||
615 | return engineOptions.getEngineDefaultHints(); | ||
616 | } | ||
617 | |||
618 | @Override | ||
619 | public QueryEvaluationHint getQueryEvaluationHint(PQuery query) { | ||
620 | return getEngineDefaultHint().overrideBy(query.getEvaluationHints()); | ||
621 | } | ||
622 | |||
623 | private QueryEvaluationHint getQueryEvaluationHint(IQuerySpecification<?> querySpecification, | ||
624 | QueryEvaluationHint optionalOverrideHints) { | ||
625 | return getQueryEvaluationHint(querySpecification.getInternalQueryRepresentation()) | ||
626 | .overrideBy(optionalOverrideHints); | ||
627 | } | ||
628 | |||
629 | private QueryEvaluationHint getQueryEvaluationHint(PQuery query, QueryEvaluationHint optionalOverrideHints) { | ||
630 | return getQueryEvaluationHint(query).overrideBy(optionalOverrideHints); | ||
631 | } | ||
632 | |||
633 | private IMatcherCapability getRequestedCapability(IQuerySpecification<?> querySpecification, | ||
634 | QueryEvaluationHint optionalOverrideHints) { | ||
635 | final QueryEvaluationHint hint = getQueryEvaluationHint(querySpecification, optionalOverrideHints); | ||
636 | return engineOptions.getQueryBackendFactory(hint) | ||
637 | .calculateRequiredCapability(querySpecification.getInternalQueryRepresentation(), hint); | ||
638 | } | ||
639 | |||
640 | @Override | ||
641 | public void prepareGroup(IQueryGroup queryGroup, final QueryEvaluationHint optionalEvaluationHints) { | ||
642 | try { | ||
643 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
644 | |||
645 | final Set<IQuerySpecification<?>> specifications = new HashSet<IQuerySpecification<?>>( | ||
646 | queryGroup.getSpecifications()); | ||
647 | final Collection<PQuery> patterns = specifications.stream().map( | ||
648 | IQuerySpecification::getInternalQueryRepresentation).collect(Collectors.toList()); | ||
649 | patterns.forEach(PQuery::ensureInitialized); | ||
650 | |||
651 | Collection<String> erroneousPatterns = patterns.stream(). | ||
652 | filter(PQueries.queryStatusPredicate(PQueryStatus.ERROR)). | ||
653 | map(PQuery::getFullyQualifiedName). | ||
654 | collect(Collectors.toList()); | ||
655 | Preconditions.checkState(erroneousPatterns.isEmpty(), "Erroneous query(s) found: %s", | ||
656 | erroneousPatterns.stream().collect(Collectors.joining(", "))); | ||
657 | |||
658 | // TODO maybe do some smarter preparation per backend? | ||
659 | try { | ||
660 | engineContext.getBaseIndex().coalesceTraversals(new Callable<Void>() { | ||
661 | @Override | ||
662 | public Void call() throws Exception { | ||
663 | for (IQuerySpecification<?> query : specifications) { | ||
664 | getResultProviderInternal(query, optionalEvaluationHints); | ||
665 | } | ||
666 | return null; | ||
667 | } | ||
668 | }); | ||
669 | } catch (InvocationTargetException ex) { | ||
670 | final Throwable cause = ex.getCause(); | ||
671 | if (cause instanceof QueryProcessingException) | ||
672 | throw (QueryProcessingException) cause; | ||
673 | if (cause instanceof ViatraQueryException) | ||
674 | throw (ViatraQueryException) cause; | ||
675 | if (cause instanceof RuntimeException) | ||
676 | throw (RuntimeException) cause; | ||
677 | assert (false); | ||
678 | } | ||
679 | } catch (QueryProcessingException e) { | ||
680 | throw new ViatraQueryException(e); | ||
681 | } | ||
682 | } | ||
683 | |||
684 | @Override | ||
685 | public QueryScope getScope() { | ||
686 | return scope; | ||
687 | } | ||
688 | |||
689 | @Override | ||
690 | public ViatraQueryEngineOptions getEngineOptions() { | ||
691 | return engineOptions; | ||
692 | } | ||
693 | |||
694 | @Override | ||
695 | public IQueryResultProvider getResultProviderOfMatcher(ViatraQueryMatcher<? extends IPatternMatch> matcher) { | ||
696 | return ((QueryResultWrapper) matcher).backend; | ||
697 | } | ||
698 | |||
699 | @Override | ||
700 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints) { | ||
701 | try { | ||
702 | return getResultProviderInternal(query, overrideHints); | ||
703 | } catch (ViatraQueryException e) { | ||
704 | getLogger().error(ERROR_ACCESSING_BACKEND, e); | ||
705 | throw e; | ||
706 | } | ||
707 | } | ||
708 | |||
709 | @Override | ||
710 | public boolean isDisposed() { | ||
711 | return disposed; | ||
712 | } | ||
713 | |||
714 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/LifecycleProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/LifecycleProvider.java new file mode 100644 index 00000000..8bbf2a66 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/LifecycleProvider.java | |||
@@ -0,0 +1,138 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.internal.engine; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | |||
13 | import org.apache.log4j.Logger; | ||
14 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | ||
15 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
16 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineLifecycleListener; | ||
17 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
18 | |||
19 | public final class LifecycleProvider extends ListenerContainer<ViatraQueryEngineLifecycleListener> implements ViatraQueryEngineLifecycleListener{ | ||
20 | |||
21 | private final Logger logger; | ||
22 | |||
23 | /** | ||
24 | * @param queryEngine | ||
25 | */ | ||
26 | public LifecycleProvider(AdvancedViatraQueryEngine queryEngine, Logger logger) { | ||
27 | this.logger = logger; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | protected void listenerAdded(ViatraQueryEngineLifecycleListener listener) { | ||
32 | logger.debug("Lifecycle listener " + listener + " added to engine."); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | protected void listenerRemoved(ViatraQueryEngineLifecycleListener listener) { | ||
37 | logger.debug("Lifecycle listener " + listener + " removed from engine."); | ||
38 | } | ||
39 | |||
40 | // public void propagateEventToListeners(Predicate<ViatraQueryEngineLifecycleListener> function) { | ||
41 | // if (!listeners.isEmpty()) { | ||
42 | // for (ViatraQueryEngineLifecycleListener listener : new ArrayList<ViatraQueryEngineLifecycleListener>(listeners)) { | ||
43 | // try { | ||
44 | // function.apply(listener); | ||
45 | // } catch (Exception ex) { | ||
46 | // logger.error( | ||
47 | // "VIATRA Query encountered an error in delivering notification to listener " | ||
48 | // + listener + ".", ex); | ||
49 | // } | ||
50 | // } | ||
51 | // } | ||
52 | // } | ||
53 | |||
54 | @Override | ||
55 | public void matcherInstantiated(final ViatraQueryMatcher<? extends IPatternMatch> matcher) { | ||
56 | if (!listeners.isEmpty()) { | ||
57 | for (ViatraQueryEngineLifecycleListener listener : new ArrayList<ViatraQueryEngineLifecycleListener>(listeners)) { | ||
58 | try { | ||
59 | listener.matcherInstantiated(matcher); | ||
60 | } catch (Exception ex) { | ||
61 | logger.error( | ||
62 | "VIATRA Query encountered an error in delivering matcher initialization notification to listener " | ||
63 | + listener + ".", ex); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | // propagateEventToListeners(new Predicate<ViatraQueryEngineLifecycleListener>() { | ||
68 | // public boolean apply(ViatraQueryEngineLifecycleListener listener) { | ||
69 | // listener.matcherInstantiated(matcher); | ||
70 | // return true; | ||
71 | // } | ||
72 | // }); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public void engineBecameTainted(String description, Throwable t) { | ||
77 | if (!listeners.isEmpty()) { | ||
78 | for (ViatraQueryEngineLifecycleListener listener : new ArrayList<ViatraQueryEngineLifecycleListener>(listeners)) { | ||
79 | try { | ||
80 | listener.engineBecameTainted(description, t); | ||
81 | } catch (Exception ex) { | ||
82 | logger.error( | ||
83 | "VIATRA Query encountered an error in delivering engine tainted notification to listener " | ||
84 | + listener + ".", ex); | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | // propagateEventToListeners(new Predicate<ViatraQueryEngineLifecycleListener>() { | ||
89 | // public boolean apply(ViatraQueryEngineLifecycleListener listener) { | ||
90 | // listener.engineBecameTainted(); | ||
91 | // return true; | ||
92 | // } | ||
93 | // }); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void engineWiped() { | ||
98 | if (!listeners.isEmpty()) { | ||
99 | for (ViatraQueryEngineLifecycleListener listener : new ArrayList<ViatraQueryEngineLifecycleListener>(listeners)) { | ||
100 | try { | ||
101 | listener.engineWiped(); | ||
102 | } catch (Exception ex) { | ||
103 | logger.error( | ||
104 | "VIATRA Query encountered an error in delivering engine wiped notification to listener " | ||
105 | + listener + ".", ex); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | // propagateEventToListeners(new Predicate<ViatraQueryEngineLifecycleListener>() { | ||
110 | // public boolean apply(ViatraQueryEngineLifecycleListener listener) { | ||
111 | // listener.engineWiped(); | ||
112 | // return true; | ||
113 | // } | ||
114 | // }); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public void engineDisposed() { | ||
119 | if (!listeners.isEmpty()) { | ||
120 | for (ViatraQueryEngineLifecycleListener listener : new ArrayList<ViatraQueryEngineLifecycleListener>(listeners)) { | ||
121 | try { | ||
122 | listener.engineDisposed(); | ||
123 | } catch (Exception ex) { | ||
124 | logger.error( | ||
125 | "VIATRA Query encountered an error in delivering engine disposed notification to listener " | ||
126 | + listener + ".", ex); | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | // propagateEventToListeners(new Predicate<ViatraQueryEngineLifecycleListener>() { | ||
131 | // public boolean apply(ViatraQueryEngineLifecycleListener listener) { | ||
132 | // listener.engineDisposed(); | ||
133 | // return true; | ||
134 | // } | ||
135 | // }); | ||
136 | } | ||
137 | |||
138 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ListenerContainer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ListenerContainer.java new file mode 100644 index 00000000..34133bed --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ListenerContainer.java | |||
@@ -0,0 +1,43 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.internal.engine; | ||
10 | |||
11 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
12 | |||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | |||
16 | public abstract class ListenerContainer<Listener> { | ||
17 | |||
18 | protected final Set<Listener> listeners; | ||
19 | |||
20 | public ListenerContainer() { | ||
21 | this.listeners = new HashSet<Listener>(); | ||
22 | } | ||
23 | |||
24 | public synchronized void addListener(Listener listener) { | ||
25 | checkArgument(listener != null, "Cannot add null listener!"); | ||
26 | boolean added = listeners.add(listener); | ||
27 | if(added) { | ||
28 | listenerAdded(listener); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | public synchronized void removeListener(Listener listener) { | ||
33 | checkArgument(listener != null, "Cannot remove null listener!"); | ||
34 | boolean removed = listeners.remove(listener); | ||
35 | if(removed) { | ||
36 | listenerRemoved(listener); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | protected abstract void listenerAdded(Listener listener); | ||
41 | |||
42 | protected abstract void listenerRemoved(Listener listener); | ||
43 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ModelUpdateProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ModelUpdateProvider.java new file mode 100644 index 00000000..1f2c27e8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ModelUpdateProvider.java | |||
@@ -0,0 +1,214 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro | ||
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.internal.engine; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.EnumMap; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | |||
19 | import org.apache.log4j.Logger; | ||
20 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | ||
21 | import tools.refinery.viatra.runtime.api.IMatchUpdateListener; | ||
22 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
23 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineLifecycleListener; | ||
24 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
25 | import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener; | ||
26 | import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener.ChangeLevel; | ||
27 | import tools.refinery.viatra.runtime.api.scope.ViatraBaseIndexChangeListener; | ||
28 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
30 | |||
31 | public final class ModelUpdateProvider extends ListenerContainer<ViatraQueryModelUpdateListener> { | ||
32 | |||
33 | private final AdvancedViatraQueryEngine queryEngine; | ||
34 | private ChangeLevel currentChange = ChangeLevel.NO_CHANGE; | ||
35 | private ChangeLevel maxLevel = ChangeLevel.NO_CHANGE; | ||
36 | private final Map<ChangeLevel, Collection<ViatraQueryModelUpdateListener>> listenerMap; | ||
37 | private final Logger logger; | ||
38 | |||
39 | public ModelUpdateProvider(AdvancedViatraQueryEngine queryEngine, Logger logger) { | ||
40 | super(); | ||
41 | this.queryEngine = queryEngine; | ||
42 | this.logger = logger; | ||
43 | listenerMap = new EnumMap<>(ChangeLevel.class); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | protected void listenerAdded(ViatraQueryModelUpdateListener listener) { | ||
48 | // check ChangeLevel | ||
49 | // create callback for given level if required | ||
50 | if(listenerMap.isEmpty()) { | ||
51 | try { | ||
52 | this.queryEngine.getBaseIndex().addBaseIndexChangeListener(indexListener); | ||
53 | // add listener to new matchers (use lifecycle listener) | ||
54 | this.queryEngine.addLifecycleListener(selfListener); | ||
55 | } catch (ViatraQueryException e) { | ||
56 | throw new IllegalStateException("Model update listener used on engine without base index", e); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | ChangeLevel changeLevel = listener.getLevel(); | ||
61 | listenerMap.computeIfAbsent(changeLevel, k -> CollectionsFactory.createSet()).add(listener); | ||
62 | // increase or keep max level of listeners | ||
63 | ChangeLevel oldMaxLevel = maxLevel; | ||
64 | maxLevel = maxLevel.changeOccured(changeLevel); | ||
65 | if(!maxLevel.equals(oldMaxLevel) && ChangeLevel.MATCHSET.compareTo(oldMaxLevel) > 0 && ChangeLevel.MATCHSET.compareTo(maxLevel) <= 0) { | ||
66 | // add matchUpdateListener to all matchers | ||
67 | for (ViatraQueryMatcher<?> matcher : this.queryEngine.getCurrentMatchers()) { | ||
68 | this.queryEngine.addMatchUpdateListener(matcher, matchSetListener, false); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | protected void listenerRemoved(ViatraQueryModelUpdateListener listener) { | ||
75 | ChangeLevel changeLevel = listener.getLevel(); | ||
76 | Collection<ViatraQueryModelUpdateListener> old = listenerMap.getOrDefault(changeLevel, Collections.emptySet()); | ||
77 | boolean removed = old.remove(listener); | ||
78 | if(removed) { | ||
79 | if (old.isEmpty()) listenerMap.remove(changeLevel); | ||
80 | } else { | ||
81 | handleUnsuccesfulRemove(listener); | ||
82 | } | ||
83 | |||
84 | updateMaxLevel(); | ||
85 | |||
86 | if(listenerMap.isEmpty()) { | ||
87 | this.queryEngine.removeLifecycleListener(selfListener); | ||
88 | removeBaseIndexChangeListener(); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | private void removeBaseIndexChangeListener() { | ||
93 | try { | ||
94 | this.queryEngine.getBaseIndex().removeBaseIndexChangeListener(indexListener); | ||
95 | } catch (ViatraQueryException e) { | ||
96 | throw new IllegalStateException("Model update listener used on engine without base index", e); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | private void updateMaxLevel() { | ||
101 | if(!listenerMap.containsKey(maxLevel)) { | ||
102 | ChangeLevel newMaxLevel = ChangeLevel.NO_CHANGE; | ||
103 | for (ChangeLevel level : new HashSet<>(listenerMap.keySet())) { | ||
104 | newMaxLevel = newMaxLevel.changeOccured(level); | ||
105 | } | ||
106 | maxLevel = newMaxLevel; | ||
107 | } | ||
108 | if(maxLevel.compareTo(ChangeLevel.MATCHSET) < 0) { | ||
109 | // remove listener from matchers | ||
110 | for (ViatraQueryMatcher<?> matcher : this.queryEngine.getCurrentMatchers()) { | ||
111 | this.queryEngine.removeMatchUpdateListener(matcher, matchSetListener); | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | private void handleUnsuccesfulRemove(ViatraQueryModelUpdateListener listener) { | ||
117 | for (Entry<ChangeLevel, Collection<ViatraQueryModelUpdateListener>> entry : listenerMap.entrySet()) { | ||
118 | Collection<ViatraQueryModelUpdateListener> existingListeners = entry.getValue(); | ||
119 | // if the listener is contained in some other bucket, remove it from there | ||
120 | if(existingListeners.remove(listener)) { | ||
121 | logger.error("Listener "+listener+" change level changed since initialization!"); | ||
122 | if (existingListeners.isEmpty()) listenerMap.remove(entry.getKey()); | ||
123 | return; // listener is contained only once | ||
124 | } | ||
125 | } | ||
126 | logger.error("Listener "+listener+" already removed from map (e.g. engine was already disposed)!"); | ||
127 | } | ||
128 | |||
129 | private void notifyListeners() { | ||
130 | |||
131 | // any change that occurs after this point should be regarded as a new event | ||
132 | // FIXME what should happen when a listener creates new notifications? | ||
133 | // -> other listeners will get events in different order | ||
134 | ChangeLevel tempLevel = currentChange; | ||
135 | currentChange = ChangeLevel.NO_CHANGE; | ||
136 | |||
137 | if(!listenerMap.isEmpty()) { | ||
138 | for (ChangeLevel level : new HashSet<>(listenerMap.keySet())) { | ||
139 | if(tempLevel.compareTo(level) >= 0) { | ||
140 | for (ViatraQueryModelUpdateListener listener : new ArrayList<>(listenerMap.get(level))) { | ||
141 | try { | ||
142 | listener.notifyChanged(tempLevel); | ||
143 | } catch (Exception ex) { | ||
144 | logger.error( | ||
145 | "VIATRA Query encountered an error in delivering model update notification to listener " | ||
146 | + listener + ".", ex); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | } else { | ||
152 | throw new IllegalStateException("Notify listeners must not be called without listeners! Maybe an update callback was not removed correctly."); | ||
153 | } | ||
154 | |||
155 | } | ||
156 | |||
157 | // model update "providers": | ||
158 | // - model: IQBase callback even if not dirty | ||
159 | // - index: IQBase dirty callback | ||
160 | private final ViatraBaseIndexChangeListener indexListener = new ViatraBaseIndexChangeListener() { | ||
161 | |||
162 | @Override | ||
163 | public boolean onlyOnIndexChange() { | ||
164 | return false; | ||
165 | } | ||
166 | |||
167 | @Override | ||
168 | public void notifyChanged(boolean indexChanged) { | ||
169 | if(indexChanged) { | ||
170 | currentChange = currentChange.changeOccured(ChangeLevel.INDEX); | ||
171 | } else { | ||
172 | currentChange = currentChange.changeOccured(ChangeLevel.MODEL); | ||
173 | } | ||
174 | notifyListeners(); | ||
175 | } | ||
176 | |||
177 | }; | ||
178 | // - matchset: add the same listener to each matcher and use a dirty flag. needs IQBase callback as well | ||
179 | private final IMatchUpdateListener<IPatternMatch> matchSetListener = new IMatchUpdateListener<IPatternMatch>() { | ||
180 | |||
181 | @Override | ||
182 | public void notifyDisappearance(IPatternMatch match) { | ||
183 | currentChange = currentChange.changeOccured(ChangeLevel.MATCHSET); | ||
184 | } | ||
185 | |||
186 | @Override | ||
187 | public void notifyAppearance(IPatternMatch match) { | ||
188 | currentChange = currentChange.changeOccured(ChangeLevel.MATCHSET); | ||
189 | } | ||
190 | }; | ||
191 | |||
192 | private final ViatraQueryEngineLifecycleListener selfListener = new ViatraQueryEngineLifecycleListener() { | ||
193 | |||
194 | @Override | ||
195 | public void matcherInstantiated(ViatraQueryMatcher<? extends IPatternMatch> matcher) { | ||
196 | if (maxLevel.compareTo(ChangeLevel.MATCHSET) >= 0) { | ||
197 | ModelUpdateProvider.this.queryEngine.addMatchUpdateListener(matcher, matchSetListener, false); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | @Override | ||
202 | public void engineWiped() {} | ||
203 | |||
204 | @Override | ||
205 | public void engineDisposed() { | ||
206 | removeBaseIndexChangeListener(); | ||
207 | listenerMap.clear(); | ||
208 | maxLevel = ChangeLevel.NO_CHANGE; | ||
209 | } | ||
210 | |||
211 | @Override | ||
212 | public void engineBecameTainted(String description, Throwable t) {} | ||
213 | }; | ||
214 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java new file mode 100644 index 00000000..83f6f766 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java | |||
@@ -0,0 +1,42 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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; | ||
10 | |||
11 | /** | ||
12 | * A common base class for all exceptions thrown by various VIATRA Query Runtime APIs. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 2.0 | ||
16 | */ | ||
17 | public abstract class ViatraQueryRuntimeException extends RuntimeException { | ||
18 | |||
19 | private static final long serialVersionUID = -8505253058035069310L; | ||
20 | |||
21 | public ViatraQueryRuntimeException() { | ||
22 | super(); | ||
23 | } | ||
24 | |||
25 | public ViatraQueryRuntimeException(String message) { | ||
26 | super(message); | ||
27 | } | ||
28 | |||
29 | public ViatraQueryRuntimeException(Throwable cause) { | ||
30 | super(cause); | ||
31 | } | ||
32 | |||
33 | public ViatraQueryRuntimeException(String message, Throwable cause) { | ||
34 | super(message, cause); | ||
35 | } | ||
36 | |||
37 | public ViatraQueryRuntimeException(String message, Throwable cause, boolean enableSuppression, | ||
38 | boolean writableStackTrace) { | ||
39 | super(message, cause, enableSuppression, writableStackTrace); | ||
40 | } | ||
41 | |||
42 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java new file mode 100644 index 00000000..2c09ede1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java | |||
@@ -0,0 +1,24 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | /** | ||
12 | * @since 2.0 | ||
13 | */ | ||
14 | class AverageAccumulator<Domain> { | ||
15 | Domain value; | ||
16 | long count; | ||
17 | |||
18 | public AverageAccumulator(Domain value, long count) { | ||
19 | super(); | ||
20 | this.value = value; | ||
21 | this.count = count; | ||
22 | } | ||
23 | |||
24 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java new file mode 100644 index 00000000..e8a26afd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class DoubleAverageOperator implements IMultisetAggregationOperator<Double, AverageAccumulator<Double>, Double> { | ||
21 | |||
22 | public static final DoubleAverageOperator INSTANCE = new DoubleAverageOperator(); | ||
23 | |||
24 | private DoubleAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Double> createNeutral() { | ||
40 | return new AverageAccumulator<Double>(0d, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Double> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Double> update(AverageAccumulator<Double> oldResult, Double updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Double> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : result.value/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Double> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToDouble(Double::doubleValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Double> clone(AverageAccumulator<Double> original) { | ||
79 | return new AverageAccumulator<Double>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java new file mode 100644 index 00000000..744b0cd1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java | |||
@@ -0,0 +1,62 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Double values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class DoubleSumOperator extends AbstractMemorylessAggregationOperator<Double, Double> { | ||
21 | public static final DoubleSumOperator INSTANCE = new DoubleSumOperator(); | ||
22 | |||
23 | private DoubleSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Double> incrementally computes the sum of java.lang.Double values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Double>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Double createNeutral() { | ||
38 | return 0d; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Double result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Double update(Double oldResult, Double updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Double aggregateStream(Stream<Double> stream) { | ||
58 | return stream.mapToDouble(Double::doubleValue).sum(); | ||
59 | } | ||
60 | |||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java new file mode 100644 index 00000000..ee4ceeb8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java | |||
@@ -0,0 +1,135 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.aggregators; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.SortedMap; | ||
13 | import java.util.TreeMap; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
17 | |||
18 | /** | ||
19 | * Incrementally computes the minimum or maximum of java.lang.Comparable values, using the default comparison | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 1.4 | ||
23 | */ | ||
24 | public class ExtremumOperator<T extends Comparable<T>> | ||
25 | implements IMultisetAggregationOperator<T, SortedMap<T, Integer>, T> { | ||
26 | |||
27 | public enum Extreme { | ||
28 | MIN, MAX; | ||
29 | |||
30 | /** | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public <T> T pickFrom(SortedMap<T, Integer> nonEmptyMultiSet) { | ||
34 | switch(this) { | ||
35 | case MIN: | ||
36 | return nonEmptyMultiSet.firstKey(); | ||
37 | case MAX: | ||
38 | return nonEmptyMultiSet.lastKey(); | ||
39 | default: | ||
40 | return null; | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | private static final ExtremumOperator MIN_OP = new ExtremumOperator<>(Extreme.MIN); | ||
46 | private static final ExtremumOperator MAX_OP = new ExtremumOperator<>(Extreme.MAX); | ||
47 | |||
48 | public static <T extends Comparable<T>> ExtremumOperator<T> getMin() { | ||
49 | return MIN_OP; | ||
50 | } | ||
51 | public static <T extends Comparable<T>> ExtremumOperator<T> getMax() { | ||
52 | return MAX_OP; | ||
53 | } | ||
54 | |||
55 | Extreme extreme; | ||
56 | private ExtremumOperator(Extreme extreme) { | ||
57 | super(); | ||
58 | this.extreme = extreme; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public String getShortDescription() { | ||
63 | String opName = getName(); | ||
64 | return String.format( | ||
65 | "%s incrementally computes the %simum of java.lang.Comparable values, using the default comparison", | ||
66 | opName, opName); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public String getName() { | ||
71 | return extreme.name().toLowerCase(); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.0 | ||
76 | */ | ||
77 | @Override | ||
78 | public SortedMap<T, Integer> createNeutral() { | ||
79 | return new TreeMap<>(); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * @since 2.0 | ||
84 | */ | ||
85 | @Override | ||
86 | public boolean isNeutral(SortedMap<T, Integer> result) { | ||
87 | return result.isEmpty(); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * @since 2.0 | ||
92 | */ | ||
93 | @Override | ||
94 | public SortedMap<T, Integer> update(SortedMap<T, Integer> oldResult, T updateValue, boolean isInsertion) { | ||
95 | oldResult.compute(updateValue, (value, c) -> { | ||
96 | int count = (c == null) ? 0 : c; | ||
97 | int result = (isInsertion) ? count+1 : count-1; | ||
98 | return (result == 0) ? null : result; | ||
99 | }); | ||
100 | return oldResult; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * @since 2.0 | ||
105 | */ | ||
106 | @Override | ||
107 | public T getAggregate(SortedMap<T, Integer> result) { | ||
108 | return result.isEmpty() ? null : | ||
109 | extreme.pickFrom(result); | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | @Override | ||
116 | public T aggregateStream(Stream<T> stream) { | ||
117 | switch (extreme) { | ||
118 | case MIN: | ||
119 | return stream.min(Comparator.naturalOrder()).orElse(null); | ||
120 | case MAX: | ||
121 | return stream.max(Comparator.naturalOrder()).orElse(null); | ||
122 | default: | ||
123 | return null; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * @since 2.4 | ||
129 | */ | ||
130 | @Override | ||
131 | public SortedMap<T, Integer> clone(SortedMap<T, Integer> original) { | ||
132 | return new TreeMap<T, Integer>(original); | ||
133 | } | ||
134 | |||
135 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java new file mode 100644 index 00000000..bf422476 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class IntegerAverageOperator implements IMultisetAggregationOperator<Integer, AverageAccumulator<Integer>, Double> { | ||
21 | |||
22 | public static final IntegerAverageOperator INSTANCE = new IntegerAverageOperator(); | ||
23 | |||
24 | private IntegerAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Integer> createNeutral() { | ||
40 | return new AverageAccumulator<Integer>(0, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Integer> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Integer> update(AverageAccumulator<Integer> oldResult, Integer updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Integer> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : ((double)result.value)/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Integer> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToInt(Integer::intValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Integer> clone(AverageAccumulator<Integer> original) { | ||
79 | return new AverageAccumulator<Integer>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java new file mode 100644 index 00000000..18584256 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Integer values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class IntegerSumOperator extends AbstractMemorylessAggregationOperator<Integer, Integer> { | ||
21 | public static final IntegerSumOperator INSTANCE = new IntegerSumOperator(); | ||
22 | |||
23 | private IntegerSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Integer> incrementally computes the sum of java.lang.Integer values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Integer>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Integer createNeutral() { | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Integer result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Integer update(Integer oldResult, Integer updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Integer aggregateStream(Stream<Integer> stream) { | ||
58 | return stream.mapToInt(Integer::intValue).sum(); | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java new file mode 100644 index 00000000..d56c9507 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class LongAverageOperator implements IMultisetAggregationOperator<Long, AverageAccumulator<Long>, Double> { | ||
21 | |||
22 | public static final LongAverageOperator INSTANCE = new LongAverageOperator(); | ||
23 | |||
24 | private LongAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Long> createNeutral() { | ||
40 | return new AverageAccumulator<Long>(0l, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Long> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Long> update(AverageAccumulator<Long> oldResult, Long updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Long> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : ((double)result.value)/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Long> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToLong(Long::longValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Long> clone(AverageAccumulator<Long> original) { | ||
79 | return new AverageAccumulator<Long>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java new file mode 100644 index 00000000..29ded090 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Long values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class LongSumOperator extends AbstractMemorylessAggregationOperator<Long, Long> { | ||
21 | public static final LongSumOperator INSTANCE = new LongSumOperator(); | ||
22 | |||
23 | private LongSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Long> incrementally computes the sum of java.lang.Long values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Long>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Long createNeutral() { | ||
38 | return 0L; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Long result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Long update(Long oldResult, Long updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Long aggregateStream(Stream<Long> stream) { | ||
58 | return stream.mapToLong(Long::longValue).sum(); | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java new file mode 100644 index 00000000..c25678aa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * This aggregator calculates the average of the values of a selected aggregate parameter of a called pattern. The aggregate | ||
17 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
18 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
19 | * summation. | ||
20 | * | ||
21 | * @since 2.0 | ||
22 | * | ||
23 | */ | ||
24 | @AggregatorType( | ||
25 | parameterTypes = {Integer.class, Double.class, Long.class}, | ||
26 | returnTypes = {Double.class, Double.class, Double.class}) | ||
27 | public final class avg implements IAggregatorFactory { | ||
28 | |||
29 | @Override | ||
30 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
31 | if (Integer.class.equals(domainClass)) | ||
32 | return new BoundAggregator(IntegerAverageOperator.INSTANCE, Integer.class, Double.class); | ||
33 | if (Double.class.equals(domainClass)) | ||
34 | return new BoundAggregator(DoubleAverageOperator.INSTANCE, Double.class, Double.class); | ||
35 | if (Long.class.equals(domainClass)) | ||
36 | return new BoundAggregator(LongAverageOperator.INSTANCE, Long.class, Double.class); | ||
37 | else throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java new file mode 100644 index 00000000..8310a0ce --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * An aggregator to count the number of matches a pattern has. The return of the aggregator is an non-negative integer | ||
17 | * number. | ||
18 | * | ||
19 | * @since 1.4 | ||
20 | * | ||
21 | */ | ||
22 | @AggregatorType(parameterTypes = {Void.class}, returnTypes = {Integer.class}) | ||
23 | public final class count implements IAggregatorFactory { | ||
24 | |||
25 | @Override | ||
26 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
27 | if (Void.class.equals(domainClass)) | ||
28 | return new BoundAggregator(null, Void.class, Integer.class); | ||
29 | else throw new IllegalArgumentException(); | ||
30 | } | ||
31 | |||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java new file mode 100644 index 00000000..e0236223 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
19 | |||
20 | /** | ||
21 | * This aggregator calculates the maximum value of a selected aggregate parameter of a called pattern. The aggregate | ||
22 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
23 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
24 | * minimum calculation. | ||
25 | * | ||
26 | * @since 1.4 | ||
27 | * @author Gabor Bergmann | ||
28 | */ | ||
29 | @AggregatorType( | ||
30 | // TODO T extends Comparable? | ||
31 | parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
32 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, | ||
33 | returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
34 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) | ||
35 | public final class max implements IAggregatorFactory { | ||
36 | |||
37 | @Override | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
39 | if (Comparable.class.isAssignableFrom(domainClass)) | ||
40 | return new BoundAggregator(ExtremumOperator.getMax(), domainClass, domainClass); | ||
41 | else throw new IllegalArgumentException(); | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java new file mode 100644 index 00000000..6408c57b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
19 | |||
20 | /** | ||
21 | * This aggregator calculates the minimum value of a selected aggregate parameter of a called pattern. The aggregate | ||
22 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
23 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
24 | * minimum calculation. | ||
25 | * | ||
26 | * @since 1.4 | ||
27 | * | ||
28 | */ | ||
29 | @AggregatorType( | ||
30 | // TODO T extends Comparable? | ||
31 | parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
32 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, | ||
33 | returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
34 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) | ||
35 | public final class min implements IAggregatorFactory { | ||
36 | |||
37 | @Override | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
39 | if (Comparable.class.isAssignableFrom(domainClass)) | ||
40 | return new BoundAggregator(ExtremumOperator.getMin(), domainClass, domainClass); | ||
41 | else throw new IllegalArgumentException(); | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java new file mode 100644 index 00000000..69ff2e75 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, 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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * This aggregator calculates the sum of the values of a selected aggregate parameter of a called pattern. The aggregate | ||
17 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
18 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
19 | * summation. | ||
20 | * | ||
21 | * @since 1.4 | ||
22 | * | ||
23 | */ | ||
24 | @AggregatorType( | ||
25 | parameterTypes = {Integer.class, Double.class, Long.class}, | ||
26 | returnTypes = {Integer.class, Double.class, Long.class}) | ||
27 | public final class sum implements IAggregatorFactory { | ||
28 | |||
29 | @Override | ||
30 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
31 | if (Integer.class.equals(domainClass)) | ||
32 | return new BoundAggregator(IntegerSumOperator.INSTANCE, Integer.class, Integer.class); | ||
33 | if (Double.class.equals(domainClass)) | ||
34 | return new BoundAggregator(DoubleSumOperator.INSTANCE, Double.class, Double.class); | ||
35 | if (Long.class.equals(domainClass)) | ||
36 | return new BoundAggregator(LongSumOperator.INSTANCE, Long.class, Long.class); | ||
37 | else throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java new file mode 100644 index 00000000..1917e909 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java | |||
@@ -0,0 +1,83 @@ | |||
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.algorithms; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.1 | ||
18 | * | ||
19 | */ | ||
20 | public class OrderedIterableMerge { | ||
21 | |||
22 | private OrderedIterableMerge() { | ||
23 | // Hidden utility class constructor | ||
24 | } | ||
25 | |||
26 | /** | ||
27 | * Lazily merges two iterables, each ordered according to a given comparator. | ||
28 | * Retains order in the result, and also eliminates any duplicates that appear in both arguments. | ||
29 | */ | ||
30 | public static <T> Iterable<T> mergeUniques(Iterable<T> first, Iterable<T> second, Comparator<T> comparator) { | ||
31 | return () -> new Iterator<T>() { | ||
32 | Iterator<T> firstIterator = first.iterator(); | ||
33 | Iterator<T> secondIterator = second.iterator(); | ||
34 | T firstItem; | ||
35 | T secondItem; | ||
36 | |||
37 | { | ||
38 | fetchFirst(); | ||
39 | fetchSecond(); | ||
40 | } | ||
41 | |||
42 | private T fetchFirst() { | ||
43 | T previous = firstItem; | ||
44 | if (firstIterator.hasNext()) | ||
45 | firstItem = firstIterator.next(); | ||
46 | else | ||
47 | firstItem = null; | ||
48 | return previous; | ||
49 | } | ||
50 | private T fetchSecond() { | ||
51 | T previous = secondItem; | ||
52 | if (secondIterator.hasNext()) | ||
53 | secondItem = secondIterator.next(); | ||
54 | else | ||
55 | secondItem = null; | ||
56 | return previous; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public boolean hasNext() { | ||
61 | return firstItem != null || secondItem != null; | ||
62 | } | ||
63 | @Override | ||
64 | public T next() { | ||
65 | if (!hasNext()) throw new NoSuchElementException(); | ||
66 | if (firstItem != null && secondItem != null) { | ||
67 | if (secondItem == firstItem) { // duplicates | ||
68 | fetchFirst(); | ||
69 | return fetchSecond(); | ||
70 | } else if (comparator.compare(firstItem, secondItem) < 0) { | ||
71 | return fetchFirst(); | ||
72 | } else { | ||
73 | return fetchSecond(); | ||
74 | } | ||
75 | } else if (firstItem != null) { | ||
76 | return fetchFirst(); | ||
77 | } else { // secondItem must be non-null | ||
78 | return fetchSecond(); | ||
79 | } | ||
80 | } | ||
81 | }; | ||
82 | } | ||
83 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java new file mode 100644 index 00000000..c69f08e5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java | |||
@@ -0,0 +1,214 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.algorithms; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | |||
20 | /** | ||
21 | * Union-find data structure implementation. Note that the implementation relies on the correct implementation of the | ||
22 | * equals method of the type parameter's class. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * | ||
26 | * @param <V> | ||
27 | * the type parameter of the element's stored in the union-find data structure | ||
28 | */ | ||
29 | public class UnionFind<V> { | ||
30 | |||
31 | private final Map<V, UnionFindNodeProperty<V>> nodeMap; | ||
32 | final Map<V, Set<V>> setMap; | ||
33 | |||
34 | /** | ||
35 | * Instantiate a new union-find data structure. | ||
36 | */ | ||
37 | public UnionFind() { | ||
38 | nodeMap = CollectionsFactory.createMap(); | ||
39 | setMap = CollectionsFactory.createMap(); | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * Instantiate a new union-find data structure with the given elements as separate sets. | ||
44 | */ | ||
45 | public UnionFind(Iterable<V> elements) { | ||
46 | this(); | ||
47 | for (V element : elements) { | ||
48 | makeSet(element); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Creates a new union set from a collection of elements. | ||
54 | * | ||
55 | * @param nodes | ||
56 | * the collection of elements | ||
57 | * @return the root element | ||
58 | */ | ||
59 | public V makeSet(Collection<V> nodes) { | ||
60 | if (!nodes.isEmpty()) { | ||
61 | Iterator<V> iterator = nodes.iterator(); | ||
62 | V root = makeSet(iterator.next()); | ||
63 | while (iterator.hasNext()) { | ||
64 | root = union(root, iterator.next()); | ||
65 | } | ||
66 | return root; | ||
67 | } else { | ||
68 | return null; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * This method creates a single set containing the given node. | ||
74 | * | ||
75 | * @param node | ||
76 | * the root node of the set | ||
77 | * @return the root element | ||
78 | */ | ||
79 | public V makeSet(V node) { | ||
80 | if (!nodeMap.containsKey(node)) { | ||
81 | UnionFindNodeProperty<V> prop = new UnionFindNodeProperty<V>(0, node); | ||
82 | nodeMap.put(node, prop); | ||
83 | Set<V> set = new HashSet<V>(); | ||
84 | set.add(node); | ||
85 | setMap.put(node, set); | ||
86 | } | ||
87 | return node; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Find method with path compression. | ||
92 | * | ||
93 | * @param node | ||
94 | * the node to find | ||
95 | * @return the root node of the set in which the given node can be found | ||
96 | */ | ||
97 | public V find(V node) { | ||
98 | UnionFindNodeProperty<V> prop = nodeMap.get(node); | ||
99 | |||
100 | if (prop != null) { | ||
101 | if (prop.parent.equals(node)) { | ||
102 | return node; | ||
103 | } else { | ||
104 | prop.parent = find(prop.parent); | ||
105 | return prop.parent; | ||
106 | } | ||
107 | } | ||
108 | return null; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the | ||
113 | * universe. | ||
114 | * | ||
115 | * @param x | ||
116 | * set or single element of the universe | ||
117 | * @param y | ||
118 | * set or single element of the universe | ||
119 | * @return the new root of the two sets | ||
120 | */ | ||
121 | public V union(V x, V y) { | ||
122 | V xRoot = find(x); | ||
123 | V yRoot = find(y); | ||
124 | |||
125 | if ((xRoot == null) || (yRoot == null)) { | ||
126 | return union( (xRoot == null) ? makeSet(x) : xRoot, (yRoot == null) ? makeSet(y) : yRoot); | ||
127 | } | ||
128 | else if (!xRoot.equals(yRoot)) { | ||
129 | UnionFindNodeProperty<V> xRootProp = nodeMap.get(xRoot); | ||
130 | UnionFindNodeProperty<V> yRootProp = nodeMap.get(yRoot); | ||
131 | |||
132 | if (xRootProp.rank < yRootProp.rank) { | ||
133 | xRootProp.parent = yRoot; | ||
134 | setMap.get(yRoot).addAll(setMap.get(xRoot)); | ||
135 | setMap.remove(xRoot); | ||
136 | return yRoot; | ||
137 | } else {// (xRootProp.rank >= yRootProp.rank) | ||
138 | yRootProp.parent = xRoot; | ||
139 | yRootProp.rank = (xRootProp.rank == yRootProp.rank) ? yRootProp.rank + 1 : yRootProp.rank; | ||
140 | setMap.get(xRoot).addAll(setMap.get(yRoot)); | ||
141 | setMap.remove(yRoot); | ||
142 | return xRoot; | ||
143 | } | ||
144 | } else { | ||
145 | return xRoot; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Places the given elements in to the same partition. | ||
151 | */ | ||
152 | public void unite(Set<V> elements) { | ||
153 | if (elements.size() > 1) { | ||
154 | V current = null; | ||
155 | for (V element : elements) { | ||
156 | if (current != null) { | ||
157 | if (getPartition(element) != null) { | ||
158 | union(current, element); | ||
159 | } | ||
160 | } else { | ||
161 | if (getPartition(element) != null) { | ||
162 | current = element; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Delete the set whose root is the given node. | ||
171 | * | ||
172 | * @param root | ||
173 | * the root node | ||
174 | */ | ||
175 | public void deleteSet(V root) { | ||
176 | // if (setMap.containsKey(root)) | ||
177 | for (V n : setMap.get(root)) { | ||
178 | nodeMap.remove(n); | ||
179 | } | ||
180 | setMap.remove(root); | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Returns if all given elements are in the same partition. | ||
185 | */ | ||
186 | public boolean isSameUnion(Set<V> elements) { | ||
187 | for (Set<V> partition : setMap.values()) { | ||
188 | if (partition.containsAll(elements)) { | ||
189 | return true; | ||
190 | } | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | |||
196 | /** | ||
197 | * Returns the partition in which the given element can be found, or null otherwise. | ||
198 | */ | ||
199 | public Set<V> getPartition(V element) { | ||
200 | V root = find(element); | ||
201 | return setMap.get(root); | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * Returns all partitions. | ||
206 | */ | ||
207 | public Collection<Set<V>> getPartitions() { | ||
208 | return setMap.values(); | ||
209 | } | ||
210 | |||
211 | public Set<V> getPartitionHeads() { | ||
212 | return setMap.keySet(); | ||
213 | } | ||
214 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java new file mode 100644 index 00000000..82852f9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.algorithms; | ||
11 | |||
12 | public class UnionFindNodeProperty<V> { | ||
13 | |||
14 | public int rank; | ||
15 | public V parent; | ||
16 | |||
17 | public UnionFindNodeProperty() { | ||
18 | this.rank = 0; | ||
19 | this.parent = null; | ||
20 | } | ||
21 | |||
22 | public UnionFindNodeProperty(int rank, V parent) { | ||
23 | super(); | ||
24 | this.rank = rank; | ||
25 | this.parent = parent; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String toString() { | ||
30 | return "[rank:" + rank + ", parent:" + parent.toString() + "]"; | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java new file mode 100644 index 00000000..317293bf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector; | ||
13 | |||
14 | /** | ||
15 | * Query evaluation hints applicable to any engine | ||
16 | * @since 1.6 | ||
17 | * | ||
18 | */ | ||
19 | public final class CommonQueryHintOptions { | ||
20 | |||
21 | private CommonQueryHintOptions() { | ||
22 | // Hiding constructor for utility class | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * This hint instructs the query backends to record trace information into the given trace collector | ||
27 | */ | ||
28 | public static final QueryHintOption<IRewriterTraceCollector> normalizationTraceCollector = | ||
29 | hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE); | ||
30 | |||
31 | // internal helper for conciseness | ||
32 | private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) { | ||
33 | return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue); | ||
34 | } | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java new file mode 100644 index 00000000..40853f46 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java | |||
@@ -0,0 +1,89 @@ | |||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | |||
15 | /** | ||
16 | * Function object that specifies how hints (including backend preferences) shall propagate through pattern calls. | ||
17 | * | ||
18 | * <p> A few useful default implementations are included as static fields. | ||
19 | * | ||
20 | * <p> As of 2.1, only suppported by the local search backend, and only if the pattern call is not flattened. | ||
21 | * | ||
22 | * @author Gabor Bergmann | ||
23 | * @since 2.1 | ||
24 | */ | ||
25 | @FunctionalInterface | ||
26 | public interface ICallDelegationStrategy { | ||
27 | |||
28 | /** | ||
29 | * Specifies how hints (including backend preferences) shall propagate through pattern calls. | ||
30 | * | ||
31 | * @param call a {@link PConstraint} in a query that calls another query. | ||
32 | * @param callerHint a hint under which the calling pattern is evaluated, | ||
33 | * @param callerBackend the actual backend evaluating the calling pattern. | ||
34 | * @param calleeHintProvider the provider of hints for the called pattern. | ||
35 | * @return the hints, including backend selection, | ||
36 | * that the backend responsible for the caller pattern must specify when | ||
37 | * requesting the {@link IQueryResultProvider} for the called pattern via {@link IQueryResultProviderAccess}. | ||
38 | */ | ||
39 | public QueryEvaluationHint transformHints(IQueryReference call, | ||
40 | QueryEvaluationHint callerHint, | ||
41 | IQueryBackend callerBackend, | ||
42 | IQueryBackendHintProvider calleeHintProvider); | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Options known for callee are used to override caller options, except the backend selection. | ||
47 | * Always use the same backend for the callee and the caller, regardless what is specified for the callee pattern. | ||
48 | * @author Gabor Bergmann | ||
49 | */ | ||
50 | public static final ICallDelegationStrategy FULL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
51 | QueryEvaluationHint calleeHint = | ||
52 | calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
53 | QueryEvaluationHint result = | ||
54 | callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); | ||
55 | |||
56 | QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( | ||
57 | null /* settings-ignorant */, callerBackend.getFactory()); | ||
58 | result = result.overrideBy(backendAdhesion); | ||
59 | return result; | ||
60 | }; | ||
61 | /** | ||
62 | * Options known for callee are used to override caller options, including the backend selection. | ||
63 | * If callee does not specify a backend requirement, the backend of the caller is kept. | ||
64 | * @author Gabor Bergmann | ||
65 | */ | ||
66 | public static final ICallDelegationStrategy PARTIAL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
67 | QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( | ||
68 | null /* settings-ignorant */, callerBackend.getFactory()); | ||
69 | |||
70 | QueryEvaluationHint result = | ||
71 | callerHint == null ? backendAdhesion : callerHint.overrideBy(backendAdhesion); | ||
72 | |||
73 | QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
74 | result = result.overrideBy(calleeHint); | ||
75 | |||
76 | return result; | ||
77 | }; | ||
78 | /** | ||
79 | * Options known for callee are used to override caller options, including the backend selection. | ||
80 | * Always use the backend specified for the callee (or the default if none), regardless of the backend of the caller. | ||
81 | * @author Gabor Bergmann | ||
82 | */ | ||
83 | public static final ICallDelegationStrategy NO_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
84 | QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
85 | return callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); | ||
86 | }; | ||
87 | |||
88 | |||
89 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java new file mode 100644 index 00000000..104b68a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.backend; | ||
10 | |||
11 | /** | ||
12 | * Implementations of this interface can be used to decide whether a matcher created by an arbitrary backend can | ||
13 | * potentially be used as a substitute for another matcher. | ||
14 | * | ||
15 | * @author Grill Balázs | ||
16 | * @since 1.4 | ||
17 | * | ||
18 | */ | ||
19 | public interface IMatcherCapability { | ||
20 | |||
21 | /** | ||
22 | * Returns true if matchers of this capability can be used as a substitute for a matcher implementing the given capability | ||
23 | */ | ||
24 | public boolean canBeSubstitute(IMatcherCapability capability); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java new file mode 100644 index 00000000..c85f10a4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * Internal interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher | ||
16 | * of the pattern with various parameters. | ||
17 | * | ||
18 | * @author Bergmann Gábor | ||
19 | * @since 0.9 | ||
20 | * @noextend This interface is not intended to be extended by users of the VIATRA framework, and should only be used by the query engine | ||
21 | */ | ||
22 | public interface IQueryBackend { | ||
23 | |||
24 | /** | ||
25 | * @return true iff this backend is incremental, i.e. it caches the results of queries for quick retrieval, | ||
26 | * and can provide update notifications on result set changes. | ||
27 | */ | ||
28 | public boolean isCaching(); | ||
29 | |||
30 | /** | ||
31 | * Returns a result provider for a given query. Repeated calls may return the same instance. | ||
32 | * @throws ViatraQueryRuntimeException | ||
33 | */ | ||
34 | public IQueryResultProvider getResultProvider(PQuery query); | ||
35 | |||
36 | /** | ||
37 | * Returns a result provider for a given query. Repeated calls may return the same instance. | ||
38 | * @param optional hints that may override engine and query defaults (as provided by {@link IQueryBackendHintProvider}). Can be null. | ||
39 | * @throws ViatraQueryRuntimeException | ||
40 | * @since 1.4 | ||
41 | */ | ||
42 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints); | ||
43 | |||
44 | /** | ||
45 | * Returns an existing result provider for a given query, if it was previously constructed, returns null otherwise. | ||
46 | * Will not construct and initialize new result providers. | ||
47 | */ | ||
48 | public IQueryResultProvider peekExistingResultProvider(PQuery query); | ||
49 | |||
50 | /** | ||
51 | * Propagates all pending updates in this query backend. The implementation of this method is optional, and it | ||
52 | * can be ignored entirely if the backend does not delay updates. | ||
53 | * @since 1.6 | ||
54 | */ | ||
55 | public void flushUpdates(); | ||
56 | |||
57 | /** | ||
58 | * Disposes the query backend. | ||
59 | */ | ||
60 | public abstract void dispose(); | ||
61 | |||
62 | /** | ||
63 | * @return the factory that created this backend, if this functionality is supported | ||
64 | * @since 2.1 | ||
65 | */ | ||
66 | public IQueryBackendFactory getFactory(); | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java new file mode 100644 index 00000000..e264ab3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * A Query Backend Factory identifies a query evaluator implementation, and can create an evaluator instance (an {@link IQueryBackend}) tied to a specific VIATRA Query engine upon request. | ||
16 | * | ||
17 | * <p> The factory is used as a lookup key for the backend instance, | ||
18 | * therefore implementors should either be singletons, or implement equals() / hashCode() accordingly. | ||
19 | * | ||
20 | * @author Bergmann Gabor | ||
21 | * | ||
22 | */ | ||
23 | public interface IQueryBackendFactory { | ||
24 | |||
25 | /** | ||
26 | * Creates a new {@link IQueryBackend} instance tied to the given context elements. | ||
27 | * | ||
28 | * @return an instance of the class returned by {@link #getBackendClass()} that operates in the given context. | ||
29 | * @since 1.5 | ||
30 | */ | ||
31 | public IQueryBackend | ||
32 | create(IQueryBackendContext context); | ||
33 | |||
34 | |||
35 | /** | ||
36 | * The backend instances created by this factory are guaranteed to conform to the returned class. | ||
37 | */ | ||
38 | public Class<? extends IQueryBackend> getBackendClass(); | ||
39 | |||
40 | /** | ||
41 | * Calculate the required capabilities, which are needed to execute the given pattern | ||
42 | * | ||
43 | * @since 1.4 | ||
44 | */ | ||
45 | public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint); | ||
46 | |||
47 | /** | ||
48 | * Returns whether the current backend is caching | ||
49 | * @since 2.0 | ||
50 | */ | ||
51 | public boolean isCaching(); | ||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java new file mode 100644 index 00000000..8787814e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.backend; | ||
10 | |||
11 | /** | ||
12 | * A provider interface for {@link IQueryBackendFactory} instances. | ||
13 | * @since 2.0 | ||
14 | */ | ||
15 | public interface IQueryBackendFactoryProvider { | ||
16 | |||
17 | /** | ||
18 | * Returns a query backend factory instance. The method should return the same instance in case of repeated calls. | ||
19 | */ | ||
20 | IQueryBackendFactory getFactory(); | ||
21 | |||
22 | /** | ||
23 | * Returns whether the given query backend should be considered as system default. If multiple backends are | ||
24 | * registered as system default, it is undefined which one will be chosen. | ||
25 | */ | ||
26 | default boolean isSystemDefaultEngine() { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Returns whether the given query backend should be considered as system default search backend. If multiple | ||
32 | * backends are registered as system default, it is undefined which one will be chosen. | ||
33 | */ | ||
34 | default boolean isSystemDefaultSearchBackend() { | ||
35 | return false; | ||
36 | } | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Returns whether the given query backend should be considered as system default caching backend. If multiple | ||
41 | * backends are registered as system default, it is undefined which one will be chosen. | ||
42 | */ | ||
43 | default boolean isSystemDefaultCachingBackend() { | ||
44 | return false; | ||
45 | } | ||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java new file mode 100644 index 00000000..9bb76349 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
12 | |||
13 | /** | ||
14 | * Provides query evaluation hints consisting of the Engine default hints and | ||
15 | * the hints provided by the pattern itself. | ||
16 | * | ||
17 | * @author Bergmann Gabor | ||
18 | * @since 0.9 | ||
19 | * @noimplement This interface is not intended to be implemented by clients, except in the tools.refinery.viatra.runtime plugin. | ||
20 | */ | ||
21 | public interface IQueryBackendHintProvider { | ||
22 | |||
23 | /** | ||
24 | * Suggests query evaluation hints regarding a query. The returned hints reflects the default hints of the | ||
25 | * query engine merged with the hints provided by the pattern itself. These can be overridden via specific | ||
26 | * advanced API of the engine. | ||
27 | * | ||
28 | * @since 1.4 | ||
29 | */ | ||
30 | QueryEvaluationHint getQueryEvaluationHint(PQuery query); | ||
31 | |||
32 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java new file mode 100644 index 00000000..cd7d050f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java | |||
@@ -0,0 +1,202 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
19 | |||
20 | /** | ||
21 | * An internal interface of the query backend that provides results of a given query. | ||
22 | * @author Bergmann Gabor | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IQueryResultProvider { | ||
26 | |||
27 | /** | ||
28 | * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. | ||
29 | * | ||
30 | * @param parameters | ||
31 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
32 | * @pre size of input array must be equal to the number of parameters. | ||
33 | * @since 2.0 | ||
34 | */ | ||
35 | public boolean hasMatch(Object[] parameters); | ||
36 | |||
37 | /** | ||
38 | * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. | ||
39 | * | ||
40 | * @param parameterSeedMask | ||
41 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
42 | * bound to a fixed value | ||
43 | * @param parameters | ||
44 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
45 | * parameterSeedMask, so that for each considered match tuple, | ||
46 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
47 | * @since 2.0 | ||
48 | */ | ||
49 | public boolean hasMatch(TupleMask parameterSeedMask, ITuple projectedParameterSeed); | ||
50 | |||
51 | /** | ||
52 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
53 | * | ||
54 | * @param parameters | ||
55 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
56 | * @pre size of input array must be equal to the number of parameters. | ||
57 | * @return the number of pattern matches found. | ||
58 | */ | ||
59 | public int countMatches(Object[] parameters); | ||
60 | |||
61 | /** | ||
62 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
63 | * | ||
64 | * @param parameterSeedMask | ||
65 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
66 | * bound to a fixed value | ||
67 | * @param parameters | ||
68 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
69 | * parameterSeedMask, so that for each considered match tuple, | ||
70 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
71 | * @return the number of pattern matches found. | ||
72 | * @since 1.7 | ||
73 | */ | ||
74 | public int countMatches(TupleMask parameterSeedMask, ITuple projectedParameterSeed); | ||
75 | |||
76 | /** | ||
77 | * Gives an estimate of the number of different groups the matches are projected into by the given mask | ||
78 | * (e.g. for an identity mask, this means the full match set size). The estimate must meet the required accuracy. | ||
79 | * | ||
80 | * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
81 | * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. | ||
82 | * However, caching backends are expected to simply return the indexed (projection) size, initialized on-demand if necessary. | ||
83 | * | ||
84 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
85 | * | ||
86 | * @return if available, an estimate of the cardinality of the projection of the match set, with the desired accuracy. | ||
87 | * | ||
88 | * @since 2.1 | ||
89 | */ | ||
90 | public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy); | ||
91 | |||
92 | /** | ||
93 | * Gives an estimate of the average size of different groups the matches are projected into by the given mask | ||
94 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is match set size). | ||
95 | * The estimate must meet the required accuracy. | ||
96 | * | ||
97 | * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
98 | * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. | ||
99 | * However, caching backends are expected to simply return the exact value from the index, initialized on-demand if necessary. | ||
100 | * | ||
101 | * <p> For an empty match set, zero is acceptable as an exact answer. | ||
102 | * | ||
103 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
104 | * | ||
105 | * @return if available, an estimate of the average size of each projection group of the match set, with the desired accuracy. | ||
106 | * | ||
107 | * @since 2.1 | ||
108 | */ | ||
109 | public default Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
110 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, this::estimateCardinality); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
115 | * Neither determinism nor randomness of selection is guaranteed. | ||
116 | * | ||
117 | * @param parameters | ||
118 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
119 | * @pre size of input array must be equal to the number of parameters. | ||
120 | * @return a match represented in the internal {@link Tuple} representation. | ||
121 | * @since 2.0 | ||
122 | */ | ||
123 | public Optional<Tuple> getOneArbitraryMatch(Object[] parameters); | ||
124 | |||
125 | /** | ||
126 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
127 | * Neither determinism nor randomness of selection is guaranteed. | ||
128 | * | ||
129 | * @param parameterSeedMask | ||
130 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
131 | * bound to a fixed value | ||
132 | * @param parameters | ||
133 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
134 | * parameterSeedMask, so that for each considered match tuple, | ||
135 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
136 | * @return a match represented in the internal {@link Tuple} representation. | ||
137 | * @since 2.0 | ||
138 | */ | ||
139 | public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters); | ||
140 | |||
141 | /** | ||
142 | * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. | ||
143 | * | ||
144 | * @param parameters | ||
145 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
146 | * @pre size of input array must be equal to the number of parameters. | ||
147 | * @return matches represented in the internal {@link Tuple} representation. | ||
148 | * @since 2.0 | ||
149 | */ | ||
150 | public Stream<Tuple> getAllMatches(Object[] parameters); | ||
151 | |||
152 | /** | ||
153 | * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. | ||
154 | * | ||
155 | * @param parameterSeedMask | ||
156 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
157 | * bound to a fixed value | ||
158 | * @param parameters | ||
159 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
160 | * parameterSeedMask, so that for each considered match tuple, | ||
161 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
162 | * @return matches represented in the internal {@link Tuple} representation. | ||
163 | * @since 2.0 | ||
164 | */ | ||
165 | public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters); | ||
166 | |||
167 | /** | ||
168 | * The underlying query evaluator backend. | ||
169 | */ | ||
170 | public IQueryBackend getQueryBackend(); | ||
171 | |||
172 | /** | ||
173 | * Internal method that registers low-level callbacks for match appearance and disappearance. | ||
174 | * | ||
175 | * <p> | ||
176 | * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a | ||
177 | * consistent state yet. Importantly, no model modification permitted during the callback. | ||
178 | * | ||
179 | * <p> | ||
180 | * The callback can be unregistered via invoking {@link #removeUpdateListener(Object)} with the same tag. | ||
181 | * | ||
182 | * @param listener | ||
183 | * the listener that will be notified of each new match that appears or disappears, starting from now. | ||
184 | * @param listenerTag | ||
185 | * a tag by which to identify the listener for later removal by {@link #removeUpdateListener(Object)}. | ||
186 | * @param fireNow | ||
187 | * if true, the insertion update allback will be immediately invoked on all current matches as a one-time effect. | ||
188 | * | ||
189 | * @throws UnsupportedOperationException if this is a non-incremental backend | ||
190 | * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) | ||
191 | */ | ||
192 | public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow); | ||
193 | |||
194 | /** | ||
195 | * Removes an existing listener previously registered with the given tag. | ||
196 | * | ||
197 | * @throws UnsupportedOperationException if this is a non-incremental backend | ||
198 | * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) | ||
199 | */ | ||
200 | public void removeUpdateListener(final Object listenerTag); | ||
201 | |||
202 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java new file mode 100644 index 00000000..baf7144a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Internal interface for the query backend to singal an update to a query result. | ||
15 | * @author Bergmann Gabor | ||
16 | * @since 0.9 | ||
17 | * | ||
18 | */ | ||
19 | public interface IUpdateable { | ||
20 | |||
21 | /** | ||
22 | * This callback method must be free of exceptions, even {@link RuntimeException}s (though not {@link Error}s). | ||
23 | * @param updateElement the tuple that is changed | ||
24 | * @param isInsertion true if the tuple appeared in the result set, false if disappeared from the result set | ||
25 | */ | ||
26 | public void update(Tuple updateElement, boolean isInsertion); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java new file mode 100644 index 00000000..eab92128 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java | |||
@@ -0,0 +1,241 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.backend; | ||
10 | |||
11 | import java.util.AbstractMap; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Objects; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | |||
20 | /** | ||
21 | * Provides VIATRA Query with additional hints on how a query should be evaluated. The same hint can be provided to multiple queries. | ||
22 | * | ||
23 | * <p> This class is immutable. Overriding options will create a new instance. | ||
24 | * | ||
25 | * <p> | ||
26 | * Here be dragons: for advanced users only. | ||
27 | * | ||
28 | * @author Bergmann Gabor | ||
29 | * | ||
30 | */ | ||
31 | public class QueryEvaluationHint { | ||
32 | |||
33 | /** | ||
34 | * @since 2.0 | ||
35 | * | ||
36 | */ | ||
37 | public enum BackendRequirement { | ||
38 | /** | ||
39 | * The current hint does not specify any backend requirement | ||
40 | */ | ||
41 | UNSPECIFIED, | ||
42 | /** | ||
43 | * The current hint specifies that the default search backend of the engine should be used | ||
44 | */ | ||
45 | DEFAULT_SEARCH, | ||
46 | /** | ||
47 | * The current hint specifies that the default caching backend of the engine should be used | ||
48 | */ | ||
49 | DEFAULT_CACHING, | ||
50 | /** | ||
51 | * The current hint specifies that a specific backend is to be used | ||
52 | */ | ||
53 | SPECIFIC | ||
54 | } | ||
55 | |||
56 | final IQueryBackendFactory queryBackendFactory; | ||
57 | final Map<QueryHintOption<?>, Object> backendHintSettings; | ||
58 | final BackendRequirement requirement; | ||
59 | |||
60 | /** | ||
61 | * Specifies the suggested query backend requirements, and value settings for additional backend-specific options. | ||
62 | * | ||
63 | * <p> | ||
64 | * The backend requirement type must not be {@link BackendRequirement#SPECIFIC} - for that case, use the constructor | ||
65 | * {@link #QueryEvaluationHint(Map, IQueryBackendFactory)}. | ||
66 | * | ||
67 | * @param backendHintSettings | ||
68 | * if non-null, each entry in the map overrides backend-specific options regarding query evaluation | ||
69 | * (null-valued map entries permitted to erase hints); passing null means default options associated with | ||
70 | * the query | ||
71 | * @param backendRequirementType | ||
72 | * defines the kind of backend requirement | ||
73 | * @since 2.0 | ||
74 | */ | ||
75 | public QueryEvaluationHint(Map<QueryHintOption<?>, Object> backendHintSettings, BackendRequirement backendRequirementType) { | ||
76 | super(); | ||
77 | Preconditions.checkArgument(backendRequirementType != null, "Specific requirement needs to be set"); | ||
78 | Preconditions.checkArgument(backendRequirementType != BackendRequirement.SPECIFIC, "Specific backend requirement needs providing a corresponding backend type"); | ||
79 | this.queryBackendFactory = null; | ||
80 | this.requirement = backendRequirementType; | ||
81 | this.backendHintSettings = (backendHintSettings == null) | ||
82 | ? Collections.<QueryHintOption<?>, Object> emptyMap() | ||
83 | : new HashMap<>(backendHintSettings); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Specifies the suggested query backend, and value settings for additional backend-specific options. The first | ||
88 | * parameter can be null; if the second parameter is null, it is expected that the other constructor is called | ||
89 | * instead with a {@link BackendRequirement#UNSPECIFIED} parameter. | ||
90 | * | ||
91 | * @param backendHintSettings | ||
92 | * if non-null, each entry in the map overrides backend-specific options regarding query evaluation | ||
93 | * (null-valued map entries permitted to erase hints); passing null means default options associated with | ||
94 | * the query | ||
95 | * @param queryBackendFactory | ||
96 | * overrides the query evaluator algorithm; passing null retains the default algorithm associated with | ||
97 | * the query | ||
98 | * @since 1.5 | ||
99 | */ | ||
100 | public QueryEvaluationHint( | ||
101 | Map<QueryHintOption<?>, Object> backendHintSettings, | ||
102 | IQueryBackendFactory queryBackendFactory) { | ||
103 | super(); | ||
104 | this.queryBackendFactory = queryBackendFactory; | ||
105 | this.requirement = (queryBackendFactory == null) ? BackendRequirement.UNSPECIFIED : BackendRequirement.SPECIFIC; | ||
106 | this.backendHintSettings = (backendHintSettings == null) | ||
107 | ? Collections.<QueryHintOption<?>, Object> emptyMap() | ||
108 | : new HashMap<>(backendHintSettings); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Returns the backend requirement described by this hint. If a specific backend is required, that can be queried by {@link #getQueryBackendFactory()}. | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | public BackendRequirement getQueryBackendRequirementType() { | ||
116 | return requirement; | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * A suggestion for choosing the query evaluator algorithm. | ||
121 | * | ||
122 | * <p> | ||
123 | * Returns null iff {@link #getQueryBackendRequirementType()} does not return {@link BackendRequirement#SPECIFIC}; | ||
124 | * in such cases a corresponding default backend is selected inside the engine | ||
125 | */ | ||
126 | public IQueryBackendFactory getQueryBackendFactory() { | ||
127 | return queryBackendFactory; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Each entry in the immutable map overrides backend-specific options regarding query evaluation. | ||
132 | * | ||
133 | * <p>The map is non-null, even if empty. | ||
134 | * Null-valued map entries are also permitted to erase hints via {@link #overrideBy(QueryEvaluationHint)}. | ||
135 | * | ||
136 | * @since 1.5 | ||
137 | */ | ||
138 | public Map<QueryHintOption<?>, Object> getBackendHintSettings() { | ||
139 | return backendHintSettings; | ||
140 | } | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Override values in this hint and return a consolidated instance. | ||
145 | * | ||
146 | * @since 1.4 | ||
147 | */ | ||
148 | public QueryEvaluationHint overrideBy(QueryEvaluationHint overridingHint){ | ||
149 | if (overridingHint == null) | ||
150 | return this; | ||
151 | |||
152 | BackendRequirement overriddenRequirement = this.getQueryBackendRequirementType(); | ||
153 | if (overridingHint.getQueryBackendRequirementType() != BackendRequirement.UNSPECIFIED) { | ||
154 | overriddenRequirement = overridingHint.getQueryBackendRequirementType(); | ||
155 | } | ||
156 | Map<QueryHintOption<?>, Object> hints = new HashMap<>(this.getBackendHintSettings()); | ||
157 | if (overridingHint.getBackendHintSettings() != null) { | ||
158 | hints.putAll(overridingHint.getBackendHintSettings()); | ||
159 | } | ||
160 | if (overriddenRequirement == BackendRequirement.SPECIFIC) { | ||
161 | IQueryBackendFactory factory = this.getQueryBackendFactory(); | ||
162 | if (overridingHint.getQueryBackendFactory() != null) { | ||
163 | factory = overridingHint.getQueryBackendFactory(); | ||
164 | } | ||
165 | return new QueryEvaluationHint(hints, factory); | ||
166 | } else { | ||
167 | return new QueryEvaluationHint(hints, overriddenRequirement); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Returns whether the given hint option is overridden. | ||
173 | * @since 1.5 | ||
174 | */ | ||
175 | public boolean isOptionOverridden(QueryHintOption<?> option) { | ||
176 | return getBackendHintSettings().containsKey(option); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Returns the value of the given hint option from the given hint collection, or null if not defined. | ||
181 | * @since 1.5 | ||
182 | */ | ||
183 | @SuppressWarnings("unchecked") | ||
184 | public <HintValue> HintValue getValueOrNull(QueryHintOption<HintValue> option) { | ||
185 | return (HintValue) getBackendHintSettings().get(option); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Returns the value of the given hint option from the given hint collection, or the default value if not defined. | ||
190 | * Intended to be called by backends to find out the definitive value that should be considered. | ||
191 | * @since 1.5 | ||
192 | */ | ||
193 | public <HintValue> HintValue getValueOrDefault(QueryHintOption<HintValue> option) { | ||
194 | return option.getValueOrDefault(this); | ||
195 | } | ||
196 | |||
197 | @Override | ||
198 | public int hashCode() { | ||
199 | return Objects.hash(backendHintSettings, queryBackendFactory, requirement); | ||
200 | } | ||
201 | |||
202 | @Override | ||
203 | public boolean equals(Object obj) { | ||
204 | if (this == obj) | ||
205 | return true; | ||
206 | if (obj == null) | ||
207 | return false; | ||
208 | if (getClass() != obj.getClass()) | ||
209 | return false; | ||
210 | QueryEvaluationHint other = (QueryEvaluationHint) obj; | ||
211 | return Objects.equals(backendHintSettings, other.backendHintSettings) | ||
212 | && | ||
213 | Objects.equals(queryBackendFactory, other.queryBackendFactory) | ||
214 | && | ||
215 | Objects.equals(requirement, other.requirement) | ||
216 | ; | ||
217 | } | ||
218 | |||
219 | @Override | ||
220 | public String toString() { | ||
221 | StringBuilder sb = new StringBuilder(); | ||
222 | |||
223 | if (getQueryBackendFactory() != null) | ||
224 | sb.append("backend: ").append(getQueryBackendFactory().getBackendClass().getSimpleName()); | ||
225 | if (! backendHintSettings.isEmpty()) { | ||
226 | sb.append("hints: "); | ||
227 | if(backendHintSettings instanceof AbstractMap){ | ||
228 | sb.append(backendHintSettings.toString()); | ||
229 | } else { | ||
230 | // we have to iterate on the contents | ||
231 | |||
232 | String joinedHintMap = backendHintSettings.entrySet().stream() | ||
233 | .map(setting -> setting.getKey() + "=" + setting.getValue()).collect(Collectors.joining(", ")); | ||
234 | sb.append('{').append(joinedHintMap).append('}'); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | final String result = sb.toString(); | ||
239 | return result.isEmpty() ? "defaults" : result; | ||
240 | } | ||
241 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java new file mode 100644 index 00000000..2c6bb4de --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java | |||
@@ -0,0 +1,122 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.backend; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.Objects; | ||
13 | |||
14 | /** | ||
15 | * Each instance of this class corresponds to a given hint option. | ||
16 | * | ||
17 | * It is recommended to expose options to clients (and query backends) as public static fields. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * @since 1.5 | ||
21 | */ | ||
22 | public class QueryHintOption<HintValue> { | ||
23 | |||
24 | private String optionQualifiedName; | ||
25 | private HintValue defaultValue; | ||
26 | |||
27 | /** | ||
28 | * Instantiates an option object with the given name and default value. | ||
29 | */ | ||
30 | public QueryHintOption(String optionQualifiedName, HintValue defaultValue) { | ||
31 | this.optionQualifiedName = optionQualifiedName; | ||
32 | this.defaultValue = defaultValue; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * This is the recommended constructor for hint options defined as static fields within an enclosing class. | ||
37 | * Combines the qualified name of the hint from the (qualified) name of the enclosing class and a local name (unique within that class). | ||
38 | */ | ||
39 | public <T extends HintValue> QueryHintOption(Class<?> optionNamespace, String optionLocalName, T defaultValue) { | ||
40 | this(String.format("%s@%s", optionLocalName, optionNamespace.getName()), defaultValue); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Returns the qualified name, a unique string identifier of the option. | ||
45 | */ | ||
46 | public String getQualifiedName() { | ||
47 | return optionQualifiedName; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Returns the default value of this hint option, which is to be used by query backends in the case no overriding value is assigned. | ||
52 | */ | ||
53 | public HintValue getDefaultValue() { | ||
54 | return defaultValue; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Returns the value of this hint option from the given hint collection, or the default value if not defined. | ||
59 | * Intended to be called by backends to find out the definitive value that should be considered. | ||
60 | */ | ||
61 | @SuppressWarnings("unchecked") | ||
62 | public HintValue getValueOrDefault(QueryEvaluationHint hints) { | ||
63 | Object value = hints.getValueOrNull(this); | ||
64 | if (value == null) | ||
65 | return getDefaultValue(); | ||
66 | else { | ||
67 | return (HintValue) value; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Returns the value of this hint option from the given hint collection, or null if not defined. | ||
74 | */ | ||
75 | public HintValue getValueOrNull(QueryEvaluationHint hints) { | ||
76 | return hints.getValueOrNull(this); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Returns whether this hint option is defined in the given hint collection. | ||
81 | */ | ||
82 | public boolean isOverriddenIn(QueryEvaluationHint hints) { | ||
83 | return hints.isOptionOverridden(this); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Puts a value of this hint option into an option-to-value map. | ||
88 | * | ||
89 | * <p> This method is offered in lieu of a builder API. | ||
90 | * Use this method on any number of hint options in order to populate an option-value map. | ||
91 | * Then instantiate the immutable {@link QueryEvaluationHint} using the map. | ||
92 | * | ||
93 | * @see #insertValueIfNondefault(Map, Object) | ||
94 | * @return the hint value that was previously present in the map under this hint option, carrying over the semantics of {@link Map#put(Object, Object)}. | ||
95 | */ | ||
96 | @SuppressWarnings("unchecked") | ||
97 | public HintValue insertOverridingValue(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) { | ||
98 | return (HintValue) hints.put(this, overridingValue); | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Puts a value of this hint option into an option-to-value map, if the given value differs from the default value of the option. | ||
103 | * If the default value is provided instead, then the map is not updated. | ||
104 | * | ||
105 | * <p> This method is offered in lieu of a builder API. | ||
106 | * Use this method on any number of hint options in order to populate an option-value map. | ||
107 | * Then instantiate the immutable {@link QueryEvaluationHint} using the map. | ||
108 | * | ||
109 | * @see #insertOverridingValue(Map, Object) | ||
110 | * @since 2.0 | ||
111 | */ | ||
112 | public void insertValueIfNondefault(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) { | ||
113 | if (!Objects.equals(defaultValue, overridingValue)) | ||
114 | hints.put(this, overridingValue); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public String toString() { | ||
119 | return optionQualifiedName; | ||
120 | } | ||
121 | |||
122 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java new file mode 100644 index 00000000..6ec6d53e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java | |||
@@ -0,0 +1,74 @@ | |||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | |||
15 | /** | ||
16 | * Uniform way of requesting result providers for pattern calls within queries. | ||
17 | * Intended users are query backends, for calling other backends to deliver results of dependee queries. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * @since 2.1 | ||
21 | */ | ||
22 | public class ResultProviderRequestor { | ||
23 | IQueryBackend callerBackend; | ||
24 | IQueryResultProviderAccess resultProviderAccess; | ||
25 | IQueryBackendHintProvider hintProvider; | ||
26 | ICallDelegationStrategy delegationStrategy; | ||
27 | QueryEvaluationHint callerHint; | ||
28 | QueryEvaluationHint universalOverride; | ||
29 | |||
30 | |||
31 | /** | ||
32 | * @param callerBackend the actual backend evaluating the calling pattern. | ||
33 | * @param resultProviderAccess | ||
34 | * @param hintProvider | ||
35 | * @param delegationStrategy | ||
36 | * @param callerHint a hint under which the calling pattern is evaluated, | ||
37 | * @param universalOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy} | ||
38 | */ | ||
39 | public ResultProviderRequestor(IQueryBackend callerBackend, IQueryResultProviderAccess resultProviderAccess, | ||
40 | IQueryBackendHintProvider hintProvider, ICallDelegationStrategy delegationStrategy, | ||
41 | QueryEvaluationHint callerHint, QueryEvaluationHint universalOverride) { | ||
42 | super(); | ||
43 | this.callerBackend = callerBackend; | ||
44 | this.resultProviderAccess = resultProviderAccess; | ||
45 | this.hintProvider = hintProvider; | ||
46 | this.delegationStrategy = delegationStrategy; | ||
47 | this.callerHint = callerHint; | ||
48 | this.universalOverride = universalOverride; | ||
49 | } | ||
50 | |||
51 | |||
52 | |||
53 | |||
54 | /** | ||
55 | * | ||
56 | * @param call a {@link PConstraint} in a query that calls another query. | ||
57 | * @param spotOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy} | ||
58 | * and the universal override specified in the constructor | ||
59 | * @return the obtained result provider | ||
60 | */ | ||
61 | public IQueryResultProvider requestResultProvider(IQueryReference call, QueryEvaluationHint spotOverride) { | ||
62 | QueryEvaluationHint hints = | ||
63 | delegationStrategy.transformHints(call, callerHint, callerBackend, hintProvider); | ||
64 | |||
65 | if (universalOverride != null) | ||
66 | hints = hints.overrideBy(universalOverride); | ||
67 | |||
68 | if (spotOverride != null) | ||
69 | hints = hints.overrideBy(spotOverride); | ||
70 | |||
71 | return resultProviderAccess.getResultProvider(call.getReferredQuery(), hints); | ||
72 | } | ||
73 | |||
74 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java new file mode 100644 index 00000000..99611758 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java | |||
@@ -0,0 +1,69 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Comparator; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | /** | ||
19 | * Common abstract class for implementers of {@link IQueryMetaContext} | ||
20 | * | ||
21 | * @author Grill Balázs | ||
22 | * @since 1.3 | ||
23 | * | ||
24 | */ | ||
25 | public abstract class AbstractQueryMetaContext implements IQueryMetaContext { | ||
26 | |||
27 | /** | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | @Override | ||
31 | public Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey) { | ||
32 | return new HashMap<>(); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @since 1.6 | ||
37 | */ | ||
38 | @Override | ||
39 | public boolean canLeadOutOfScope(IInputKey key) { | ||
40 | return key.getArity() > 1; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * @since 1.6 | ||
45 | */ | ||
46 | @Override | ||
47 | public Comparator<IInputKey> getSuggestedEliminationOrdering() { | ||
48 | return (o1, o2) -> 0; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * @since 1.6 | ||
53 | */ | ||
54 | @Override | ||
55 | public Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey) { | ||
56 | return Collections.emptySet(); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public boolean isPosetKey(IInputKey key) { | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public IPosetComparator getPosetComparator(Iterable<IInputKey> key) { | ||
66 | return null; | ||
67 | } | ||
68 | |||
69 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java new file mode 100644 index 00000000..c797eff9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.context; | ||
10 | |||
11 | /** | ||
12 | * This class is intended to be extended by implementors. The main purpose of this abstract implementation to protect | ||
13 | * implementors from future changes in the interface. | ||
14 | * | ||
15 | * @author Grill Balázs | ||
16 | * @since 1.4 | ||
17 | * | ||
18 | */ | ||
19 | public abstract class AbstractQueryRuntimeContext implements IQueryRuntimeContext { | ||
20 | |||
21 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java new file mode 100644 index 00000000..4dbcca88 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java | |||
@@ -0,0 +1,45 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | /** | ||
12 | * An input key identifies an input (extensional) relation, such as the instance set of a given node or edge type, or the direct containment relation. | ||
13 | * | ||
14 | * <p> The input key, at the very minimum, is associated with an arity (number of columns), a user-friendly name, and a string identifier (for distributive purposes). | ||
15 | * | ||
16 | * <p> The input key itself must be an immutable data object that properly overrides equals() and hashCode(). | ||
17 | * It must be instantiable without using the query context object, so that query specifications may construct the appropriate PQueries. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public interface IInputKey { | ||
23 | |||
24 | /** | ||
25 | * A user-friendly name that can be shown on screen for debug purposes, included in exceptions, etc. | ||
26 | */ | ||
27 | public String getPrettyPrintableName(); | ||
28 | /** | ||
29 | * An internal string identifier that can be used to uniquely identify to input key (relevant for distributed applications). | ||
30 | */ | ||
31 | public String getStringID(); | ||
32 | |||
33 | /** | ||
34 | * The width of tuples in this relation. | ||
35 | */ | ||
36 | public int getArity(); | ||
37 | |||
38 | /** | ||
39 | * Returns true iff instance tuples of the key can be enumerated. | ||
40 | * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. | ||
41 | */ | ||
42 | boolean isEnumerable(); | ||
43 | |||
44 | |||
45 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java new file mode 100644 index 00000000..e2d5bcee --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Implementations of this interface aid the query engine with the ordering of poset elements. This information is | ||
15 | * particularly important in the delete and re-derive evaluation mode because they let the engine identify monotone | ||
16 | * change pairs. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 1.6 | ||
20 | */ | ||
21 | public interface IPosetComparator { | ||
22 | |||
23 | /** | ||
24 | * Returns true if the 'left' tuple of poset elements is smaller or equal than the 'right' tuple of poset elements according to | ||
25 | * the partial order that this poset comparator employs. | ||
26 | * | ||
27 | * @param left | ||
28 | * the left tuple of poset elements | ||
29 | * @param right | ||
30 | * the right tuple of poset elements | ||
31 | * @return true if left is smaller or equal to right, false otherwise | ||
32 | */ | ||
33 | public boolean isLessOrEqual(final Tuple left, final Tuple right); | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java new file mode 100644 index 00000000..04f00aaa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs 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.context; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * This interface is a collector which holds every API that is provided by the engine to control | ||
20 | * the operation of the backends. | ||
21 | * | ||
22 | * @since 1.5 | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IQueryBackendContext { | ||
26 | |||
27 | Logger getLogger(); | ||
28 | |||
29 | IQueryRuntimeContext getRuntimeContext(); | ||
30 | |||
31 | IQueryCacheContext getQueryCacheContext(); | ||
32 | |||
33 | IQueryBackendHintProvider getHintProvider(); | ||
34 | |||
35 | IQueryResultProviderAccess getResultProviderAccess(); | ||
36 | |||
37 | QueryAnalyzer getQueryAnalyzer(); | ||
38 | |||
39 | /** | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | IMatcherCapability getRequiredMatcherCapability(PQuery query, QueryEvaluationHint overrideHints); | ||
43 | |||
44 | /** | ||
45 | * @since 1.6 | ||
46 | */ | ||
47 | boolean areUpdatesDelayed(); | ||
48 | |||
49 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java new file mode 100644 index 00000000..617207f6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * Provides information on already cached queries to query evaluator backends at runtime. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public interface IQueryCacheContext { | ||
23 | |||
24 | /** | ||
25 | * Checks if there already is a caching result provider for the given query. | ||
26 | * <p> Returns false if called while the caching result provider of the given query is being constructed in the first place. | ||
27 | */ | ||
28 | public boolean isResultCached(PQuery query); | ||
29 | |||
30 | /** | ||
31 | * Returns a caching result provider for the given query; it must be constructed if it does not exist yet. | ||
32 | * <p> <b>Caution:</b> behavior undefined if called while the caching result provider of the given query is being constructed. | ||
33 | * Beware of infinite loops. | ||
34 | * <p> <b>Postcondition:</b> {@link IQueryBackend#isCaching()} returns true for the {@link #getQueryBackend()} of the returned provider | ||
35 | * | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | */ | ||
38 | public IQueryResultProvider getCachingResultProvider(PQuery query); | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java new file mode 100644 index 00000000..4c22a3cb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | /** | ||
17 | * Provides metamodel information (relationship of input keys) to query evaluator backends at runtime and at query planning time. | ||
18 | * | ||
19 | * @noimplement Implementors should extend {@link AbstractQueryMetaContext} instead of directly implementing this interface. | ||
20 | * @author Bergmann Gabor | ||
21 | */ | ||
22 | public interface IQueryMetaContext { | ||
23 | |||
24 | /** | ||
25 | * Returns true iff instance tuples of the given key can be enumerated. | ||
26 | * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. | ||
27 | * <p> Equivalent to {@link IInputKey#isEnumerable()}. | ||
28 | */ | ||
29 | boolean isEnumerable(IInputKey key); | ||
30 | |||
31 | /** | ||
32 | * Returns true iff the set of instance tuples of the given key is immutable. | ||
33 | * <p> If false, the runtime provides notifications upon change. | ||
34 | */ | ||
35 | boolean isStateless(IInputKey key); | ||
36 | |||
37 | /** | ||
38 | * Returns a set of implications (weakened alternatives), | ||
39 | * with a suggestion for the query planner that satisfying them first may help in satisfying the implying key. | ||
40 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. | ||
41 | * <p> Must follow directly or transitively from implications of {@link #getImplications(IInputKey)}. | ||
42 | * @since 1.6 | ||
43 | */ | ||
44 | Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey); | ||
45 | |||
46 | /** | ||
47 | * Returns known direct implications, e.g. edge supertypes, edge opposites, node type constraints, etc. | ||
48 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. | ||
49 | */ | ||
50 | Collection<InputKeyImplication> getImplications(IInputKey implyingKey); | ||
51 | |||
52 | /** | ||
53 | * Returns known "double dispatch" implications, where the given implying key implies other input keys under certain additional conditions (themselves input keys). | ||
54 | * For example, a "type x, unscoped" input key may imply the "type x, in scope" input key under the condition of the input key "x is in scope" | ||
55 | * | ||
56 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys (either as the implying key or as the additional condition). | ||
57 | * <p> Note that symmetry is not required, i.e. the additional conditions do not have to list the same conditional implication. | ||
58 | * @return multi-map, where the keys are additional conditions and the values are input key implications jointly implied by the condition and the given implying key. | ||
59 | * @since 2.0 | ||
60 | */ | ||
61 | Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey); | ||
62 | |||
63 | /** | ||
64 | * Returns functional dependencies of the input key expressed in terms of column indices. | ||
65 | * | ||
66 | * <p> Each entry of the map is a functional dependency rule, where the entry key specifies source columns and the entry value specifies target columns. | ||
67 | */ | ||
68 | Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key); | ||
69 | |||
70 | /** | ||
71 | * For query normalizing, this is the order suggested for trying to eliminate input keys. | ||
72 | * @since 1.6 | ||
73 | */ | ||
74 | Comparator<IInputKey> getSuggestedEliminationOrdering(); | ||
75 | |||
76 | /** | ||
77 | * Tells whether the given {@link IInputKey} is an edge and may lead out of scope. | ||
78 | * | ||
79 | * @since 1.6 | ||
80 | */ | ||
81 | boolean canLeadOutOfScope(IInputKey key); | ||
82 | |||
83 | /** | ||
84 | * Returns true if the given {@link IInputKey} represents a poset type. | ||
85 | * @since 1.6 | ||
86 | */ | ||
87 | boolean isPosetKey(IInputKey key); | ||
88 | |||
89 | /** | ||
90 | * Returns an {@link IPosetComparator} for the given set of {@link IInputKey}s. | ||
91 | * | ||
92 | * @param keys an iterable collection of input keys | ||
93 | * @return the poset comparator | ||
94 | * @since 1.6 | ||
95 | */ | ||
96 | IPosetComparator getPosetComparator(Iterable<IInputKey> keys); | ||
97 | |||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java new file mode 100644 index 00000000..7fecd01a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs 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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | |||
15 | /** | ||
16 | * This interface exposes API to request {@link IQueryResultProvider} for {@link PQuery} instances. | ||
17 | * | ||
18 | * @author Grill Balázs | ||
19 | * @since 1.5 | ||
20 | * @noimplement This interface is not intended to be implemented by clients. | ||
21 | */ | ||
22 | public interface IQueryResultProviderAccess { | ||
23 | |||
24 | /** | ||
25 | * Get a result provider for the given {@link PQuery}, which conforms the capabilities requested by the | ||
26 | * given {@link QueryEvaluationHint} object. | ||
27 | * @throws ViatraQueryRuntimeException | ||
28 | */ | ||
29 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints); | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java new file mode 100644 index 00000000..61359c1b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java | |||
@@ -0,0 +1,287 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.matchers.context; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.CancellationToken; | ||
13 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
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.util.Accuracy; | ||
18 | |||
19 | import java.lang.reflect.InvocationTargetException; | ||
20 | import java.util.Optional; | ||
21 | import java.util.concurrent.Callable; | ||
22 | |||
23 | /** | ||
24 | * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. | ||
25 | * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. | ||
26 | * | ||
27 | * @author Bergmann Gabor | ||
28 | * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. | ||
29 | */ | ||
30 | public interface IQueryRuntimeContext { | ||
31 | /** | ||
32 | * Provides metamodel-specific info independent of the runtime instance model. | ||
33 | */ | ||
34 | public IQueryMetaContext getMetaContext(); | ||
35 | |||
36 | |||
37 | /** | ||
38 | * The given callable will be executed, and all model traversals will be delayed until the execution is done. If | ||
39 | * there are any outstanding information to be read from the model, a single coalesced model traversal will | ||
40 | * initialize the caches and deliver the notifications. | ||
41 | * | ||
42 | * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. | ||
43 | * | ||
44 | * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. | ||
45 | * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. | ||
46 | * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). | ||
47 | * | ||
48 | * @param callable | ||
49 | */ | ||
50 | public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; | ||
51 | /** | ||
52 | * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). | ||
53 | */ | ||
54 | public boolean isCoalescing(); | ||
55 | |||
56 | /** | ||
57 | * Returns true if index is available for the given key providing the given service. | ||
58 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
59 | * @since 1.4 | ||
60 | */ | ||
61 | public boolean isIndexed(IInputKey key, IndexingService service); | ||
62 | |||
63 | /** | ||
64 | * If the given (enumerable) input key is not yet indexed, the model will be traversed | ||
65 | * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) | ||
66 | * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging | ||
67 | * multiple indexing requests to an appropriate level. | ||
68 | * | ||
69 | * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key | ||
70 | * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. | ||
71 | * | ||
72 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
73 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
74 | * @since 1.4 | ||
75 | */ | ||
76 | public void ensureIndexed(IInputKey key, IndexingService service); | ||
77 | |||
78 | /** | ||
79 | * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. | ||
80 | * | ||
81 | * @param key an input key | ||
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 once in seedMask. | ||
85 | * @param seed | ||
86 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
87 | * parameterSeedMask, so that for each considered match tuple, | ||
88 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
89 | * | ||
90 | * @return the number of tuples in the model for the given key and seed | ||
91 | * | ||
92 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
93 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
94 | * @since 1.7 | ||
95 | */ | ||
96 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); | ||
97 | |||
98 | |||
99 | /** | ||
100 | * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask | ||
101 | * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. | ||
102 | * | ||
103 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | ||
104 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. | ||
105 | * | ||
106 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
107 | * | ||
108 | * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. | ||
109 | * | ||
110 | * @since 2.1 | ||
111 | */ | ||
112 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); | ||
113 | |||
114 | |||
115 | /** | ||
116 | * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask | ||
117 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). | ||
118 | * The estimate must meet the required accuracy. | ||
119 | * | ||
120 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | ||
121 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
122 | * | ||
123 | * <p> For an empty relation, zero is acceptable as an exact answer. | ||
124 | * | ||
125 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
126 | * | ||
127 | * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. | ||
128 | * | ||
129 | * @since 2.1 | ||
130 | */ | ||
131 | public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
132 | if (key.isEnumerable()) { | ||
133 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, | ||
134 | (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); | ||
135 | } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); | ||
136 | } | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
141 | * | ||
142 | * @param key an input key | ||
143 | * @param seedMask | ||
144 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
145 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. | ||
146 | * @param seed | ||
147 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
148 | * parameterSeedMask, so that for each considered match tuple, | ||
149 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
150 | * @return the tuples in the model for the given key and seed | ||
151 | * | ||
152 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
153 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
154 | * @since 1.7 | ||
155 | */ | ||
156 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); | ||
157 | |||
158 | /** | ||
159 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | ||
160 | * are bound by the seed except for one. | ||
161 | * | ||
162 | * <p> | ||
163 | * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given | ||
164 | * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. | ||
165 | * | ||
166 | * @param key | ||
167 | * an input key | ||
168 | * @param seedMask | ||
169 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
170 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
171 | * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. | ||
172 | * @param seed | ||
173 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
174 | * parameterSeedMask, so that for each considered match tuple, | ||
175 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
176 | * @return the objects in the model for the given key and seed | ||
177 | * | ||
178 | * <p> | ||
179 | * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
180 | * @throws IllegalArgumentException | ||
181 | * if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
182 | * @since 1.7 | ||
183 | */ | ||
184 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); | ||
185 | |||
186 | /** | ||
187 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | ||
188 | * are bound by the seed. | ||
189 | * | ||
190 | * <p> | ||
191 | * Returns whether the given tuple is in the extensional relation identified by the input key. | ||
192 | * | ||
193 | * <p> | ||
194 | * Note: this call works for non-enumerable input keys as well. | ||
195 | * | ||
196 | * @param key | ||
197 | * an input key | ||
198 | * @param seed | ||
199 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
200 | * parameterSeedMask, so that for each considered match tuple, | ||
201 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
202 | * @return true iff there is at least a single tuple contained in the relation that corresponds to the seed tuple | ||
203 | * @since 2.0 | ||
204 | */ | ||
205 | public boolean containsTuple(IInputKey key, ITuple seed); | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
210 | * <p> This should be called after invoking | ||
211 | * | ||
212 | * @param key an input key | ||
213 | * @param seed can be null or a tuple with matching arity; | ||
214 | * if non-null, only those updates in the model are notified about | ||
215 | * that match the seed at positions where the seed is non-null. | ||
216 | * @param listener will be notified of future changes | ||
217 | * | ||
218 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
219 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
220 | */ | ||
221 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); | ||
222 | |||
223 | /** | ||
224 | * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
225 | * | ||
226 | * @param key an input key | ||
227 | * @param seed can be null or a tuple with matching arity; | ||
228 | * if non-null, only those updates in the model are notified about | ||
229 | * that match the seed at positions where the seed is non-null. | ||
230 | * @param listener will no longer be notified of future changes | ||
231 | * | ||
232 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
233 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
234 | */ | ||
235 | public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); | ||
236 | /* | ||
237 | TODO: uniqueness | ||
238 | */ | ||
239 | |||
240 | /** | ||
241 | * Wraps the external element into the internal representation that is to be used by the query backend | ||
242 | * <p> model element -> internal object. | ||
243 | * <p> null must be mapped to null. | ||
244 | */ | ||
245 | public Object wrapElement(Object externalElement); | ||
246 | |||
247 | /** | ||
248 | * Unwraps the internal representation of the element into its original form | ||
249 | * <p> internal object -> model element | ||
250 | * <p> null must be mapped to null. | ||
251 | */ | ||
252 | public Object unwrapElement(Object internalElement); | ||
253 | |||
254 | /** | ||
255 | * Unwraps the tuple of elements into the internal representation that is to be used by the query backend | ||
256 | * <p> model elements -> internal objects | ||
257 | * <p> null must be mapped to null. | ||
258 | */ | ||
259 | public Tuple wrapTuple(Tuple externalElements); | ||
260 | |||
261 | /** | ||
262 | * Unwraps the tuple of internal representations of elements into their original forms | ||
263 | * <p> internal objects -> model elements | ||
264 | * <p> null must be mapped to null. | ||
265 | */ | ||
266 | public Tuple unwrapTuple(Tuple internalElements); | ||
267 | |||
268 | /** | ||
269 | * Starts wildcard indexing for the given service. After this call, no registration is required for this {@link IndexingService}. | ||
270 | * a previously set wildcard level cannot be lowered, only extended. | ||
271 | * @since 1.4 | ||
272 | */ | ||
273 | public void ensureWildcardIndexing(IndexingService service); | ||
274 | |||
275 | /** | ||
276 | * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as | ||
277 | * the indexing is finished. The callback is executed only once, then is removed from the callback queue. | ||
278 | * @param traversalCallback | ||
279 | * @throws InvocationTargetException | ||
280 | * @since 1.4 | ||
281 | */ | ||
282 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; | ||
283 | |||
284 | default CancellationToken getCancellationToken() { | ||
285 | return CancellationToken.NONE; | ||
286 | } | ||
287 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java new file mode 100644 index 00000000..7be27d56 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Listens for changes in the runtime context. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public interface IQueryRuntimeContextListener { | ||
19 | |||
20 | /** | ||
21 | * The given tuple was inserted into or removed from the input relation indicated by the given key. | ||
22 | * @param key the key identifying the input relation that was updated | ||
23 | * @param updateTuple the tuple that was inserted or removed | ||
24 | * @param isInsertion true if it was an insertion, false otherwise. | ||
25 | */ | ||
26 | public void update(IInputKey key, Tuple updateTuple, boolean isInsertion); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java new file mode 100644 index 00000000..8210765d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.context; | ||
10 | |||
11 | /** | ||
12 | * These are the different services which can be provided by an {@link IQueryRuntimeContext} implementation. | ||
13 | * | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public enum IndexingService { | ||
19 | |||
20 | /** | ||
21 | * Cardinality information is available. Makes possible to calculate | ||
22 | * unseeded calls of {@link IQueryRuntimeContext#countTuples(IInputKey, tools.refinery.viatra.runtime.matchers.tuple.Tuple)} | ||
23 | */ | ||
24 | STATISTICS, | ||
25 | |||
26 | /** | ||
27 | * The indexer can provide notifications about changes in the model. | ||
28 | */ | ||
29 | NOTIFICATIONS, | ||
30 | |||
31 | /** | ||
32 | * Enables enumeration of instances and reverse-navigation. | ||
33 | */ | ||
34 | INSTANCES | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java new file mode 100644 index 00000000..2a403810 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java | |||
@@ -0,0 +1,103 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | |||
15 | /** | ||
16 | * Data object representing the implication of an input key, in use cases including edge supertypes, edge opposites, node type constraints, etc. | ||
17 | * | ||
18 | * <p> Each instance tuple of the <i>implying input key</i> (if given) implies the presence of an instance tuple of the <i>implied input key</i> consisting of elements of the original tuple at given positions. | ||
19 | * When the input key is null, it is not an input constraint but some other source that implies input keys. | ||
20 | * | ||
21 | * <p> The implication is an immutable data object. | ||
22 | * | ||
23 | * @author Bergmann Gabor | ||
24 | * | ||
25 | */ | ||
26 | public final class InputKeyImplication { | ||
27 | private IInputKey implyingKey; | ||
28 | private IInputKey impliedKey; | ||
29 | private List<Integer> impliedIndices; | ||
30 | |||
31 | /** | ||
32 | * Optional. Instance tuples of this input key imply an instance tuple of another key. | ||
33 | * Sometimes it is not an input key that implies other input keys, so this attribute can be null. | ||
34 | */ | ||
35 | public IInputKey getImplyingKey() { | ||
36 | return implyingKey; | ||
37 | } | ||
38 | /** | ||
39 | * An instance tuple of this input key is implied by another key. | ||
40 | */ | ||
41 | public IInputKey getImpliedKey() { | ||
42 | return impliedKey; | ||
43 | } | ||
44 | /** | ||
45 | * The implied instance tuple consists of the values in the implying tuple at these indices. | ||
46 | */ | ||
47 | public List<Integer> getImpliedIndices() { | ||
48 | return impliedIndices; | ||
49 | } | ||
50 | /** | ||
51 | * @param implyingKey instance tuples of this input key imply an instance tuple of the other key. | ||
52 | * @param impliedKey instance tuple of this input key is implied by the other key. | ||
53 | * @param implyingIndices the implied instance tuple consists of the values in the implying tuple at these indices. | ||
54 | */ | ||
55 | public InputKeyImplication(IInputKey implyingKey, IInputKey impliedKey, | ||
56 | List<Integer> implyingIndices) { | ||
57 | super(); | ||
58 | this.implyingKey = implyingKey; | ||
59 | this.impliedKey = impliedKey; | ||
60 | this.impliedIndices = Collections.unmodifiableList(new ArrayList<>(implyingIndices)); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public int hashCode() { | ||
65 | final int prime = 31; | ||
66 | int result = 1; | ||
67 | result = prime * result | ||
68 | + ((impliedIndices == null) ? 0 : impliedIndices.hashCode()); | ||
69 | result = prime * result | ||
70 | + ((impliedKey == null) ? 0 : impliedKey.hashCode()); | ||
71 | result = prime * result | ||
72 | + ((implyingKey == null) ? 0 : implyingKey.hashCode()); | ||
73 | return result; | ||
74 | } | ||
75 | @Override | ||
76 | public boolean equals(Object obj) { | ||
77 | if (this == obj) | ||
78 | return true; | ||
79 | if (obj == null) | ||
80 | return false; | ||
81 | if (!(obj instanceof InputKeyImplication)) | ||
82 | return false; | ||
83 | InputKeyImplication other = (InputKeyImplication) obj; | ||
84 | if (impliedIndices == null) { | ||
85 | if (other.impliedIndices != null) | ||
86 | return false; | ||
87 | } else if (!impliedIndices.equals(other.impliedIndices)) | ||
88 | return false; | ||
89 | if (impliedKey == null) { | ||
90 | if (other.impliedKey != null) | ||
91 | return false; | ||
92 | } else if (!impliedKey.equals(other.impliedKey)) | ||
93 | return false; | ||
94 | if (implyingKey == null) { | ||
95 | if (other.implyingKey != null) | ||
96 | return false; | ||
97 | } else if (!implyingKey.equals(other.implyingKey)) | ||
98 | return false; | ||
99 | return true; | ||
100 | } | ||
101 | |||
102 | |||
103 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java new file mode 100644 index 00000000..f9b05e5b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context.common; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | |||
13 | |||
14 | /** | ||
15 | * An input key that is identified by a single wrapped object and the class of the wrapper. | ||
16 | * @author Bergmann Gabor | ||
17 | * | ||
18 | */ | ||
19 | public abstract class BaseInputKeyWrapper<Wrapped> implements IInputKey { | ||
20 | protected Wrapped wrappedKey; | ||
21 | |||
22 | public BaseInputKeyWrapper(Wrapped wrappedKey) { | ||
23 | super(); | ||
24 | this.wrappedKey = wrappedKey; | ||
25 | } | ||
26 | |||
27 | public Wrapped getWrappedKey() { | ||
28 | return wrappedKey; | ||
29 | } | ||
30 | |||
31 | |||
32 | @Override | ||
33 | public int hashCode() { | ||
34 | return ((wrappedKey == null) ? 0 : wrappedKey.hashCode()); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public boolean equals(Object obj) { | ||
39 | if (this == obj) | ||
40 | return true; | ||
41 | if (obj == null) | ||
42 | return false; | ||
43 | if (!(this.getClass().equals(obj.getClass()))) | ||
44 | return false; | ||
45 | BaseInputKeyWrapper other = (BaseInputKeyWrapper) obj; | ||
46 | if (wrappedKey == null) { | ||
47 | if (other.wrappedKey != null) | ||
48 | return false; | ||
49 | } else if (!wrappedKey.equals(other.wrappedKey)) | ||
50 | return false; | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java new file mode 100644 index 00000000..eb972c2d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java | |||
@@ -0,0 +1,165 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.context.common; | ||
10 | |||
11 | |||
12 | |||
13 | /** | ||
14 | * Instance tuples are of form (x), where object x is an instance of the given Java class or its subclasses. | ||
15 | * <p> Fine print 1: classes with the same name are considered equivalent. | ||
16 | * Can be instantiated with class name, even if the class itself is not loaded yet; but if the class is available, passing it in the constructor is beneficial to avoid classloading troubles. | ||
17 | * <p> Fine print 2: primitive types (char, etc.) are transparently treated as their wrapper class (Character, etc.). | ||
18 | * <p> Non-enumerable type, can only be checked. | ||
19 | * <p> Stateless type (objects can't change their type) | ||
20 | * @author Bergmann Gabor | ||
21 | * | ||
22 | */ | ||
23 | public class JavaTransitiveInstancesKey extends BaseInputKeyWrapper<String> { | ||
24 | |||
25 | /** | ||
26 | * The actual Class whose (transitive) instances this relation contains. Can be null at compile time, if only the name is available. | ||
27 | * Can be a primitive. | ||
28 | */ | ||
29 | private Class<?> cachedOriginalInstanceClass; | ||
30 | |||
31 | /** | ||
32 | * Same as {@link #cachedOriginalInstanceClass}, but primitive classes are replaced with their wrapper classes (e.g. int --> java.lang.Integer). | ||
33 | */ | ||
34 | private Class<?> cachedWrapperInstanceClass; | ||
35 | |||
36 | /** | ||
37 | * Preferred constructor. | ||
38 | */ | ||
39 | public JavaTransitiveInstancesKey(Class<?> instanceClass) { | ||
40 | this(primitiveTypeToWrapperClass(instanceClass).getName()); | ||
41 | this.cachedOriginalInstanceClass = instanceClass; | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Call this constructor only in contexts where the class itself is not available for loading, e.g. it has not yet been compiled. | ||
46 | */ | ||
47 | public JavaTransitiveInstancesKey(String className) { | ||
48 | super(className); | ||
49 | } | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Returns null if class cannot be loaded. | ||
54 | */ | ||
55 | private Class<?> getOriginalInstanceClass() { | ||
56 | if (cachedOriginalInstanceClass == null) { | ||
57 | try { | ||
58 | resolveClassInternal(); | ||
59 | } catch (ClassNotFoundException e) { | ||
60 | // class not yet available at this point | ||
61 | } | ||
62 | } | ||
63 | return cachedOriginalInstanceClass; | ||
64 | } | ||
65 | |||
66 | |||
67 | /** | ||
68 | * @return non-null instance class | ||
69 | * @throws ClassNotFoundException | ||
70 | */ | ||
71 | private Class<?> forceGetOriginalInstanceClass() throws ClassNotFoundException { | ||
72 | if (cachedOriginalInstanceClass == null) { | ||
73 | resolveClassInternal(); | ||
74 | } | ||
75 | return cachedOriginalInstanceClass; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @return non-null instance class, wrapped if primitive class | ||
80 | * @throws ClassNotFoundException | ||
81 | */ | ||
82 | public Class<?> forceGetWrapperInstanceClass() throws ClassNotFoundException { | ||
83 | forceGetOriginalInstanceClass(); | ||
84 | return getWrapperInstanceClass(); | ||
85 | } | ||
86 | /** | ||
87 | * @return non-null instance class, wrapped if primitive class | ||
88 | * @throws ClassNotFoundException | ||
89 | */ | ||
90 | public Class<?> forceGetInstanceClass() throws ClassNotFoundException { | ||
91 | return forceGetWrapperInstanceClass(); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * @return instance class, wrapped if primitive class, null if class cannot be loaded | ||
96 | */ | ||
97 | public Class<?> getWrapperInstanceClass() { | ||
98 | if (cachedWrapperInstanceClass == null) { | ||
99 | cachedWrapperInstanceClass = primitiveTypeToWrapperClass(getOriginalInstanceClass()); | ||
100 | } | ||
101 | return cachedWrapperInstanceClass; | ||
102 | } | ||
103 | /** | ||
104 | * @return instance class, wrapped if primitive class, null if class cannot be loaded | ||
105 | */ | ||
106 | public Class<?> getInstanceClass() { | ||
107 | return getWrapperInstanceClass(); | ||
108 | } | ||
109 | |||
110 | private void resolveClassInternal() throws ClassNotFoundException { | ||
111 | cachedOriginalInstanceClass = Class.forName(wrappedKey); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public String getPrettyPrintableName() { | ||
116 | getWrapperInstanceClass(); | ||
117 | return cachedWrapperInstanceClass == null ? wrappedKey == null ? "<null>" : wrappedKey : cachedWrapperInstanceClass.getName(); | ||
118 | } | ||
119 | |||
120 | @Override | ||
121 | public String getStringID() { | ||
122 | return "javaClass#"+ getPrettyPrintableName(); | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public int getArity() { | ||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | @Override | ||
131 | public boolean isEnumerable() { | ||
132 | return false; | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public String toString() { | ||
137 | return this.getPrettyPrintableName(); | ||
138 | } | ||
139 | |||
140 | private static Class<?> primitiveTypeToWrapperClass(Class<?> instanceClass) { | ||
141 | if (instanceClass != null && instanceClass.isPrimitive()) { | ||
142 | if (Void.TYPE.equals(instanceClass)) | ||
143 | return Void.class; | ||
144 | if (Boolean.TYPE.equals(instanceClass)) | ||
145 | return Boolean.class; | ||
146 | if (Character.TYPE.equals(instanceClass)) | ||
147 | return Character.class; | ||
148 | if (Byte.TYPE.equals(instanceClass)) | ||
149 | return Byte.class; | ||
150 | if (Short.TYPE.equals(instanceClass)) | ||
151 | return Short.class; | ||
152 | if (Integer.TYPE.equals(instanceClass)) | ||
153 | return Integer.class; | ||
154 | if (Long.TYPE.equals(instanceClass)) | ||
155 | return Long.class; | ||
156 | if (Float.TYPE.equals(instanceClass)) | ||
157 | return Float.class; | ||
158 | if (Double.TYPE.equals(instanceClass)) | ||
159 | return Double.class; | ||
160 | } | ||
161 | return instanceClass; | ||
162 | } | ||
163 | |||
164 | |||
165 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java new file mode 100644 index 00000000..bb24ec8c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java | |||
@@ -0,0 +1,153 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Abel Hegedus, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.context.surrogate; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.NoSuchElementException; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; | ||
23 | |||
24 | /** | ||
25 | * @author Abel Hegedus | ||
26 | * | ||
27 | */ | ||
28 | public class SurrogateQueryRegistry { | ||
29 | |||
30 | private Map<IInputKey, IProvider<PQuery>> registeredSurrogateQueryMap = new HashMap<>(); | ||
31 | private Map<IInputKey, IProvider<PQuery>> dynamicSurrogateQueryMap = new HashMap<>(); | ||
32 | |||
33 | /** | ||
34 | * Hidden constructor | ||
35 | */ | ||
36 | private SurrogateQueryRegistry() { | ||
37 | } | ||
38 | |||
39 | private static final SurrogateQueryRegistry INSTANCE = new SurrogateQueryRegistry(); | ||
40 | |||
41 | public static SurrogateQueryRegistry instance() { | ||
42 | return INSTANCE; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * | ||
47 | * @param feature | ||
48 | * @param surrogateQuery | ||
49 | * @return the previous surrogate query associated with feature, or null if there was no such query FQN registered | ||
50 | * @throws IllegalArgumentException if feature or surrogateQuery is null | ||
51 | */ | ||
52 | public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { | ||
53 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query must not be null!"); | ||
54 | return registerSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery)); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * | ||
59 | * @param feature | ||
60 | * @param surrogateQuery | ||
61 | * @return the previous surrogate query associated with feature, or null | ||
62 | * if there was no such query registered | ||
63 | * @throws IllegalArgumentException | ||
64 | * if feature or surrogateQuery is null | ||
65 | */ | ||
66 | public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQueryProvider) { | ||
67 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
68 | Preconditions.checkArgument(surrogateQueryProvider != null, "Surrogate query must not be null!"); | ||
69 | return registeredSurrogateQueryMap.put(feature, surrogateQueryProvider); | ||
70 | } | ||
71 | |||
72 | public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { | ||
73 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); | ||
74 | return addDynamicSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery)); | ||
75 | } | ||
76 | |||
77 | public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQuery) { | ||
78 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
79 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); | ||
80 | return dynamicSurrogateQueryMap.put(feature, surrogateQuery); | ||
81 | } | ||
82 | |||
83 | public IProvider<PQuery> removeDynamicSurrogateQueryForFeature(IInputKey feature) { | ||
84 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
85 | return dynamicSurrogateQueryMap.remove(feature); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * | ||
90 | * @param feature that may have surrogate query defined, null not allowed | ||
91 | * @return true if the feature has a surrogate query defined | ||
92 | * @throws IllegalArgumentException if feature is null | ||
93 | */ | ||
94 | public boolean hasSurrogateQueryFQN(IInputKey feature) { | ||
95 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
96 | boolean surrogateExists = dynamicSurrogateQueryMap.containsKey(feature); | ||
97 | if(!surrogateExists){ | ||
98 | surrogateExists = registeredSurrogateQueryMap.containsKey(feature); | ||
99 | } | ||
100 | return surrogateExists; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * | ||
105 | * @param feature for which the surrogate query FQN should be returned | ||
106 | * @return the surrogate query FQN defined for the feature | ||
107 | * @throws IllegalArgumentException if feature is null | ||
108 | * @throws NoSuchElementException if the feature has no surrogate query defined, use {@link #hasSurrogateQueryFQN} to check | ||
109 | */ | ||
110 | public PQuery getSurrogateQuery(IInputKey feature) { | ||
111 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
112 | IProvider<PQuery> surrogate = dynamicSurrogateQueryMap.get(feature); | ||
113 | if(surrogate == null) { | ||
114 | surrogate = registeredSurrogateQueryMap.get(feature); | ||
115 | } | ||
116 | if(surrogate != null) { | ||
117 | return surrogate.get(); | ||
118 | } else { | ||
119 | throw new NoSuchElementException(String.format("Feature %s has no surrogate query defined! Use #hasSurrogateQueryFQN to check existence.", feature)); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @return an unmodifiable set of features with registered surrogate queries | ||
125 | */ | ||
126 | public Set<IInputKey> getRegisteredSurrogateQueries() { | ||
127 | return Collections.unmodifiableSet(getRegisteredSurrogateQueriesInternal()); | ||
128 | } | ||
129 | |||
130 | private Set<IInputKey> getRegisteredSurrogateQueriesInternal() { | ||
131 | return registeredSurrogateQueryMap.keySet(); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * @return an unmodifiable set of features with dynamically added surrogate queries | ||
136 | */ | ||
137 | public Set<IInputKey> getDynamicSurrogateQueries() { | ||
138 | return Collections.unmodifiableSet(getDynamicSurrogateQueriesInternal()); | ||
139 | } | ||
140 | |||
141 | private Set<IInputKey> getDynamicSurrogateQueriesInternal() { | ||
142 | return dynamicSurrogateQueryMap.keySet(); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * @return an unmodifiable set that contains all features with surrogate queries. | ||
147 | */ | ||
148 | public Set<IInputKey> getAllSurrogateQueries() { | ||
149 | Set<IInputKey> results = new HashSet<>(getRegisteredSurrogateQueriesInternal()); | ||
150 | results.addAll(getDynamicSurrogateQueriesInternal()); | ||
151 | return results; | ||
152 | } | ||
153 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java new file mode 100644 index 00000000..66587f77 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java | |||
@@ -0,0 +1,58 @@ | |||
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.memories; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
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.util.CollectionsFactory; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IMemory; | ||
21 | |||
22 | /** | ||
23 | * Common parts of nullary and identity specializations. | ||
24 | * | ||
25 | * @noextend This class is not intended to be subclassed by clients. | ||
26 | * @author Gabor Bergmann | ||
27 | * @since 2.0 | ||
28 | */ | ||
29 | abstract class AbstractTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
30 | |||
31 | protected IMemory<Tuple> tuples; | ||
32 | |||
33 | protected AbstractTrivialMaskedMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
34 | super(mask, owner); | ||
35 | tuples = CollectionsFactory.createMemory(Object.class, bucketType); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
40 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void clear() { | ||
45 | tuples.clear(); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public int getTotalSize() { | ||
50 | return tuples.size(); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Iterator<Tuple> iterator() { | ||
55 | return tuples.iterator(); | ||
56 | } | ||
57 | |||
58 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java new file mode 100644 index 00000000..92081409 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java | |||
@@ -0,0 +1,127 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.memories; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
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.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | * Default implementation that covers all cases. | ||
30 | * | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public final class DefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
34 | extends MaskedTupleMemory<Timestamp> { | ||
35 | /** | ||
36 | * Maps a signature tuple to the bucket of tuples with the given signature. | ||
37 | * | ||
38 | * @since 2.0 | ||
39 | */ | ||
40 | protected IMultiLookup<Tuple, Tuple> signatureToTuples; | ||
41 | |||
42 | /** | ||
43 | * @param mask | ||
44 | * The mask used to index the matchings | ||
45 | * @param owner | ||
46 | * the object "owning" this memory | ||
47 | * @param bucketType | ||
48 | * the kind of tuple collection maintained for each indexer bucket | ||
49 | * @since 2.0 | ||
50 | */ | ||
51 | public DefaultMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
52 | super(mask, owner); | ||
53 | signatureToTuples = CollectionsFactory.<Tuple, Tuple> createMultiLookup(Object.class, bucketType, Object.class); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean add(Tuple tuple) { | ||
58 | Tuple signature = mask.transform(tuple); | ||
59 | return add(tuple, signature); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean add(Tuple tuple, Tuple signature) { | ||
64 | try { | ||
65 | return signatureToTuples.addPair(signature, tuple) == ChangeGranularity.KEY; | ||
66 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
67 | throw raiseDuplicateInsertion(tuple); | ||
68 | } | ||
69 | |||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean remove(Tuple tuple) { | ||
74 | Tuple signature = mask.transform(tuple); | ||
75 | return remove(tuple, signature); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean remove(Tuple tuple, Tuple signature) { | ||
80 | try { | ||
81 | return signatureToTuples.removePair(signature, tuple) == ChangeGranularity.KEY; | ||
82 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
83 | throw raiseDuplicateDeletion(tuple); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
89 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public Collection<Tuple> get(ITuple signature) { | ||
94 | IMemoryView<Tuple> bucket = signatureToTuples.lookupUnsafe(signature); | ||
95 | return bucket == null ? null : bucket.distinctValues(); | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public void clear() { | ||
100 | signatureToTuples.clear(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Iterable<Tuple> getSignatures() { | ||
105 | return signatureToTuples.distinctKeys(); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public Iterator<Tuple> iterator() { | ||
110 | return signatureToTuples.distinctValues().iterator(); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public int getTotalSize() { | ||
115 | int i = 0; | ||
116 | for (Tuple key : signatureToTuples.distinctKeys()) { | ||
117 | i += signatureToTuples.lookup(key).size(); | ||
118 | } | ||
119 | return i; | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public int getKeysetSize() { | ||
124 | return signatureToTuples.countKeys(); | ||
125 | } | ||
126 | |||
127 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java new file mode 100644 index 00000000..dc59daf5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java | |||
@@ -0,0 +1,77 @@ | |||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
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.util.CollectionsFactory.MemoryType; | ||
18 | |||
19 | /** | ||
20 | * Specialized for identity mask; tuples are stored as a simple set/multiset memory. | ||
21 | * | ||
22 | * @author Gabor Bergmann | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | public final class IdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> { | ||
26 | |||
27 | /** | ||
28 | * @param mask | ||
29 | * The mask used to index the matchings | ||
30 | * @param owner the object "owning" this memory | ||
31 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
32 | * @since 2.0 | ||
33 | */ | ||
34 | public IdentityMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
35 | super(mask, bucketType, owner); | ||
36 | if (!mask.isIdentity()) throw new IllegalArgumentException(mask.toString()); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public int getKeysetSize() { | ||
41 | return tuples.size(); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return tuples; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Collection<Tuple> get(ITuple signature) { | ||
51 | Tuple contained = tuples.theContainedVersionOfUnsafe(signature); | ||
52 | return contained != null ? | ||
53 | Collections.singleton(contained) : | ||
54 | null; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public boolean remove(Tuple tuple, Tuple signature) { | ||
59 | return tuples.removeOne(tuple); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean remove(Tuple tuple) { | ||
64 | return tuples.removeOne(tuple); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean add(Tuple tuple, Tuple signature) { | ||
69 | return tuples.addOne(tuple); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean add(Tuple tuple) { | ||
74 | return tuples.addOne(tuple); | ||
75 | } | ||
76 | |||
77 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java new file mode 100644 index 00000000..62377624 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java | |||
@@ -0,0 +1,385 @@ | |||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory; | ||
18 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory; | ||
19 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory; | ||
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.Clearable; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
28 | |||
29 | /** | ||
30 | * Indexes a collection of tuples by their signature (i.e. footprint, projection) obtained according to a mask. May | ||
31 | * belong to an "owner" (for documentation / traceability purposes). | ||
32 | * <p> | ||
33 | * There are timeless and timely versions of the different memories. Timely versions associate {@link Timeline}s with | ||
34 | * the stored tuples. | ||
35 | * | ||
36 | * @noextend This class is not intended to be subclassed by clients. | ||
37 | * @author Gabor Bergmann | ||
38 | * @author Tamas Szabo | ||
39 | * @since 2.0 | ||
40 | */ | ||
41 | public abstract class MaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
42 | implements Clearable, MaskedResumable<Timestamp> { | ||
43 | |||
44 | /** | ||
45 | * Creates a new memory for the given owner that indexes tuples according to the given mask. | ||
46 | */ | ||
47 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
48 | final MemoryType bucketType, final Object owner) { | ||
49 | return create(mask, bucketType, owner, false); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if | ||
54 | * the created memory should be timely or not. <br> | ||
55 | * <br> | ||
56 | * Timely means that tuples are associated with a timeline. | ||
57 | * | ||
58 | * @since 2.3 | ||
59 | */ | ||
60 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
61 | final MemoryType bucketType, final Object owner, final boolean isTimely) { | ||
62 | return create(mask, bucketType, owner, isTimely, false); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if | ||
67 | * the created memory should be timely or not. In case of timely memory, clients can also specify if the memory is | ||
68 | * lazy or not. <br> | ||
69 | * <br> | ||
70 | * Timely means that tuples are associated with a timeline. <br> | ||
71 | * <br> | ||
72 | * Lazyness can only be used together with timely memories. It means that the maintenance of the timelines is lazy, | ||
73 | * that is, the memory only updates its internal data structures at the timestamp affected by an update, and can be | ||
74 | * instructed later to resume the maintenance at higher timestamps, as well. | ||
75 | * | ||
76 | * @since 2.4 | ||
77 | */ | ||
78 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
79 | final MemoryType bucketType, final Object owner, final boolean isTimely, final boolean isLazy) { | ||
80 | if (isTimely) { | ||
81 | if (bucketType != MemoryType.SETS) { | ||
82 | throw new IllegalArgumentException("Timely memories only support SETS as the bucket type!"); | ||
83 | } | ||
84 | if (mask.isIdentity()) { | ||
85 | return new TimelyIdentityMaskedTupleMemory<T>(mask, owner, isLazy); | ||
86 | } else if (0 == mask.getSize()) { | ||
87 | return new TimelyNullaryMaskedTupleMemory<T>(mask, owner, isLazy); | ||
88 | } else if (1 == mask.getSize()) { | ||
89 | return new TimelyUnaryMaskedTupleMemory<T>(mask, owner, isLazy); | ||
90 | } else { | ||
91 | return new TimelyDefaultMaskedTupleMemory<T>(mask, owner, isLazy); | ||
92 | } | ||
93 | } else { | ||
94 | if (isLazy) { | ||
95 | throw new IllegalArgumentException("Lazy maintenance is only supported by timely memories!"); | ||
96 | } | ||
97 | if (mask.isIdentity()) { | ||
98 | return new IdentityMaskedTupleMemory<T>(mask, bucketType, owner); | ||
99 | } else if (0 == mask.getSize()) { | ||
100 | return new NullaryMaskedTupleMemory<T>(mask, bucketType, owner); | ||
101 | } else if (1 == mask.getSize()) { | ||
102 | return new UnaryMaskedTupleMemory<T>(mask, bucketType, owner); | ||
103 | } else { | ||
104 | return new DefaultMaskedTupleMemory<T>(mask, bucketType, owner); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
111 | throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public Iterable<Tuple> getResumableSignatures() { | ||
116 | throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public Timestamp getResumableTimestamp() { | ||
121 | return null; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Initializes the contents of this memory based on the contents of another memory. The default value is associated | ||
126 | * with each tuple in the timely memories. | ||
127 | * | ||
128 | * @since 2.3 | ||
129 | */ | ||
130 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
131 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Returns true if there is any tuple with the given signature that is present at the timestamp +inf, false | ||
136 | * otherwise. | ||
137 | * @since 2.4 | ||
138 | */ | ||
139 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
140 | return get(signature) != null; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Returns true of this memory is timely, false otherwise. | ||
145 | * | ||
146 | * @since 2.3 | ||
147 | */ | ||
148 | public boolean isTimely() { | ||
149 | return false; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * The mask by which the tuples are indexed. | ||
154 | */ | ||
155 | protected final TupleMask mask; | ||
156 | |||
157 | /** | ||
158 | * The object "owning" this memory. May be null. | ||
159 | * | ||
160 | * @since 1.7 | ||
161 | */ | ||
162 | protected final Object owner; | ||
163 | |||
164 | /** | ||
165 | * The node owning this memory. May be null. | ||
166 | * | ||
167 | * @since 2.0 | ||
168 | */ | ||
169 | public Object getOwner() { | ||
170 | return owner; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * The mask according to which tuples are projected and indexed. | ||
175 | * | ||
176 | * @since 2.0 | ||
177 | */ | ||
178 | public TupleMask getMask() { | ||
179 | return mask; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * @return the number of distinct signatures of all stored tuples. | ||
184 | */ | ||
185 | public abstract int getKeysetSize(); | ||
186 | |||
187 | /** | ||
188 | * @return the total number of distinct tuples stored. Multiple copies of the same tuple, if allowed, are counted as | ||
189 | * one. | ||
190 | * | ||
191 | * <p> | ||
192 | * This is currently not cached but computed on demand. It is therefore not efficient, and shall only be | ||
193 | * used for debug / profiling purposes. | ||
194 | */ | ||
195 | public abstract int getTotalSize(); | ||
196 | |||
197 | /** | ||
198 | * Iterates over distinct tuples stored in the memory, regardless of their signatures. | ||
199 | */ | ||
200 | public abstract Iterator<Tuple> iterator(); | ||
201 | |||
202 | /** | ||
203 | * Retrieves a read-only view of exactly those signatures for which at least one tuple is stored | ||
204 | * | ||
205 | * @since 2.0 | ||
206 | */ | ||
207 | public abstract Iterable<Tuple> getSignatures(); | ||
208 | |||
209 | /** | ||
210 | * Retrieves tuples that have the specified signature | ||
211 | * | ||
212 | * @return collection of tuples found, null if none | ||
213 | */ | ||
214 | public abstract Collection<Tuple> get(final ITuple signature); | ||
215 | |||
216 | /** | ||
217 | * Retrieves the tuples and their associated timelines that have the specified signature. | ||
218 | * | ||
219 | * @return the mappings from tuples to timelines, null if there is no mapping for the signature | ||
220 | * @since 2.4 | ||
221 | */ | ||
222 | public abstract Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature); | ||
223 | |||
224 | /** | ||
225 | * Retrieves tuples that have the specified signature. | ||
226 | * | ||
227 | * @return collection of tuples found, never null | ||
228 | * @since 2.1 | ||
229 | */ | ||
230 | public Collection<Tuple> getOrEmpty(final ITuple signature) { | ||
231 | final Collection<Tuple> result = get(signature); | ||
232 | return result == null ? Collections.emptySet() : result; | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * Retrieves tuples with their associated timelines that have the specified signature. | ||
237 | * | ||
238 | * @return map of tuples and timelines found, never null | ||
239 | * @since 2.4 | ||
240 | */ | ||
241 | public Map<Tuple, Timeline<Timestamp>> getOrEmptyWithTimeline(final ITuple signature) { | ||
242 | final Map<Tuple, Timeline<Timestamp>> result = getWithTimeline(signature); | ||
243 | return result == null ? Collections.emptyMap() : result; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * Removes a tuple occurrence from the memory with the given signature. | ||
248 | * | ||
249 | * @param tuple | ||
250 | * the tuple to be removed from the memory | ||
251 | * @param signature | ||
252 | * precomputed footprint of the tuple according to the mask | ||
253 | * | ||
254 | * @return true if this was the the last occurrence of the signature (according to the mask) | ||
255 | */ | ||
256 | public boolean remove(final Tuple tuple, final Tuple signature) { | ||
257 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * Removes a tuple occurrence from the memory with the given signature and timestamp. | ||
262 | * | ||
263 | * @param tuple | ||
264 | * the tuple to be removed from the memory | ||
265 | * @param signature | ||
266 | * precomputed footprint of the tuple according to the mask | ||
267 | * @param timestamp | ||
268 | * the timestamp associated with the tuple | ||
269 | * | ||
270 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
271 | * | ||
272 | * @since 2.4 | ||
273 | */ | ||
274 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
275 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * Removes a tuple occurrence from the memory. | ||
280 | * | ||
281 | * @param tuple | ||
282 | * the tuple to be removed from the memory | ||
283 | * | ||
284 | * @return true if this was the the last occurrence of the signature (according to the mask) | ||
285 | */ | ||
286 | public boolean remove(final Tuple tuple) { | ||
287 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * Removes a tuple occurrence from the memory with the given timestamp. | ||
292 | * | ||
293 | * @param tuple | ||
294 | * the tuple to be removed from the memory | ||
295 | * @param timestamp | ||
296 | * the timestamp associated with the tuple | ||
297 | * | ||
298 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
299 | * | ||
300 | * @since 2.4 | ||
301 | */ | ||
302 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
303 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Adds a tuple occurrence to the memory with the given signature. | ||
308 | * | ||
309 | * @param tuple | ||
310 | * the tuple to be added to the memory | ||
311 | * @param signature | ||
312 | * precomputed footprint of the tuple according to the mask | ||
313 | * | ||
314 | * @return true if new signature encountered (according to the mask) | ||
315 | */ | ||
316 | public boolean add(final Tuple tuple, final Tuple signature) { | ||
317 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * Adds a tuple occurrence to the memory with the given signature and timestamp. | ||
322 | * | ||
323 | * @param tuple | ||
324 | * the tuple to be added to the memory | ||
325 | * @param signature | ||
326 | * precomputed footprint of the tuple according to the mask | ||
327 | * @param timestamp | ||
328 | * the timestamp associated with the tuple | ||
329 | * | ||
330 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
331 | * | ||
332 | * @since 2.4 | ||
333 | */ | ||
334 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
335 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * Adds a tuple occurrence to the memory. | ||
340 | * | ||
341 | * @param tuple | ||
342 | * the tuple to be added to the memory | ||
343 | * | ||
344 | * @return true if new signature encountered (according to the mask) | ||
345 | */ | ||
346 | public boolean add(final Tuple tuple) { | ||
347 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Adds a tuple occurrence to the memory with the given timestamp. | ||
352 | * | ||
353 | * @param tuple | ||
354 | * the tuple to be added to the memory | ||
355 | * @param timestamp | ||
356 | * the timestamp associated with the tuple | ||
357 | * | ||
358 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
359 | * | ||
360 | * @since 2.4 | ||
361 | */ | ||
362 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
363 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
364 | } | ||
365 | |||
366 | protected MaskedTupleMemory(final TupleMask mask, final Object owner) { | ||
367 | super(); | ||
368 | this.mask = mask; | ||
369 | this.owner = owner; | ||
370 | } | ||
371 | |||
372 | protected IllegalStateException raiseDuplicateInsertion(final Tuple tuple) { | ||
373 | return new IllegalStateException(String.format("Duplicate insertion of tuple %s into %s", tuple, owner)); | ||
374 | } | ||
375 | |||
376 | protected IllegalStateException raiseDuplicateDeletion(final Tuple tuple) { | ||
377 | return new IllegalStateException(String.format("Duplicate deletion of tuple %s from %s", tuple, owner)); | ||
378 | } | ||
379 | |||
380 | @Override | ||
381 | public String toString() { | ||
382 | return getClass().getSimpleName() + "<" + mask + ">@" + owner; | ||
383 | } | ||
384 | |||
385 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java new file mode 100644 index 00000000..7fa9e053 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java | |||
@@ -0,0 +1,85 @@ | |||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
20 | |||
21 | /** | ||
22 | * Specialized for nullary mask; tuples are stored as a simple set/multiset memory. | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | public final class NullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> { | ||
28 | |||
29 | protected static final Set<Tuple> UNIT_RELATION = | ||
30 | Collections.singleton(Tuples.staticArityFlatTupleOf()); | ||
31 | protected static final Set<Tuple> EMPTY_RELATION = | ||
32 | Collections.emptySet(); | ||
33 | /** | ||
34 | * @param mask | ||
35 | * The mask used to index the matchings | ||
36 | * @param owner the object "owning" this memory | ||
37 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
38 | * @since 2.0 | ||
39 | */ | ||
40 | public NullaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
41 | super(mask, bucketType, owner); | ||
42 | if (0 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getKeysetSize() { | ||
47 | return tuples.isEmpty() ? 0 : 1; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Iterable<Tuple> getSignatures() { | ||
52 | return tuples.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public Collection<Tuple> get(ITuple signature) { | ||
57 | if (0 == signature.getSize()) | ||
58 | return tuples.distinctValues(); | ||
59 | else return null; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean remove(Tuple tuple, Tuple signature) { | ||
64 | tuples.removeOne(tuple); | ||
65 | return tuples.isEmpty(); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public boolean remove(Tuple tuple) { | ||
70 | return remove(tuple, null); | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public boolean add(Tuple tuple, Tuple signature) { | ||
75 | boolean wasEmpty = tuples.isEmpty(); | ||
76 | tuples.addOne(tuple); | ||
77 | return wasEmpty; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public boolean add(Tuple tuple) { | ||
82 | return add(tuple, null); | ||
83 | } | ||
84 | |||
85 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java new file mode 100644 index 00000000..f34cc9e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java | |||
@@ -0,0 +1,143 @@ | |||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * Specialized for unary mask; tuples are indexed by a single column as opposed to a projection (signature) tuple. | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | * @since 2.0 | ||
31 | */ | ||
32 | public final class UnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
33 | |||
34 | protected IMultiLookup<Object, Tuple> columnToTuples; | ||
35 | protected final int keyPosition; | ||
36 | |||
37 | /** | ||
38 | * @param mask | ||
39 | * The mask used to index the matchings | ||
40 | * @param owner the object "owning" this memory | ||
41 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
42 | * @since 2.0 | ||
43 | */ | ||
44 | public UnaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
45 | super(mask, owner); | ||
46 | if (1 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); | ||
47 | |||
48 | columnToTuples = CollectionsFactory.<Object, Tuple>createMultiLookup( | ||
49 | Object.class, bucketType, Object.class); | ||
50 | keyPosition = mask.indices[0]; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void clear() { | ||
55 | columnToTuples.clear(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public int getKeysetSize() { | ||
60 | return columnToTuples.countKeys(); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public int getTotalSize() { | ||
65 | int i = 0; | ||
66 | for (Object key : columnToTuples.distinctKeys()) { | ||
67 | i += columnToTuples.lookup(key).size(); | ||
68 | } | ||
69 | return i; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Iterator<Tuple> iterator() { | ||
74 | return columnToTuples.distinctValues().iterator(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Iterable<Tuple> getSignatures() { | ||
79 | return () -> { | ||
80 | Iterator<Object> wrapped = columnToTuples.distinctKeys().iterator(); | ||
81 | return new Iterator<Tuple>() { | ||
82 | @Override | ||
83 | public boolean hasNext() { | ||
84 | return wrapped.hasNext(); | ||
85 | } | ||
86 | @Override | ||
87 | public Tuple next() { | ||
88 | Object key = wrapped.next(); | ||
89 | return Tuples.staticArityFlatTupleOf(key); | ||
90 | } | ||
91 | }; | ||
92 | }; | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Collection<Tuple> get(ITuple signature) { | ||
97 | Object key = signature.get(0); | ||
98 | IMemoryView<Tuple> bucket = columnToTuples.lookup(key); | ||
99 | return bucket == null ? null : bucket.distinctValues(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
104 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
105 | } | ||
106 | |||
107 | @Override | ||
108 | public boolean remove(Tuple tuple, Tuple signature) { | ||
109 | return removeInternal(tuple, tuple.get(keyPosition)); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public boolean remove(Tuple tuple) { | ||
114 | return removeInternal(tuple, tuple.get(keyPosition)); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public boolean add(Tuple tuple, Tuple signature) { | ||
119 | return addInternal(tuple, tuple.get(keyPosition)); | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public boolean add(Tuple tuple) { | ||
124 | return addInternal(tuple, tuple.get(keyPosition)); | ||
125 | } | ||
126 | |||
127 | protected boolean addInternal(Tuple tuple, Object key) { | ||
128 | try { | ||
129 | return columnToTuples.addPair(key, tuple) == ChangeGranularity.KEY; | ||
130 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
131 | throw raiseDuplicateInsertion(tuple); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | protected boolean removeInternal(Tuple tuple, Object key) { | ||
136 | try { | ||
137 | return columnToTuples.removePair(key, tuple) == ChangeGranularity.KEY; | ||
138 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
139 | throw raiseDuplicateDeletion(tuple); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java new file mode 100644 index 00000000..45ce3a4e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java | |||
@@ -0,0 +1,228 @@ | |||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.TreeMap; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
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.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
28 | |||
29 | /** | ||
30 | * Common parts of timely default and timely unary implementations. | ||
31 | * | ||
32 | * @noextend This class is not intended to be subclassed by clients. | ||
33 | * @author Tamas Szabo | ||
34 | * @since 2.3 | ||
35 | */ | ||
36 | abstract class AbstractTimelyMaskedMemory<Timestamp extends Comparable<Timestamp>, KeyType> | ||
37 | extends MaskedTupleMemory<Timestamp> { | ||
38 | |||
39 | protected final TreeMap<Timestamp, Set<KeyType>> foldingStates; | ||
40 | protected final Map<KeyType, TimelyMemory<Timestamp>> memoryMap; | ||
41 | protected final boolean isLazy; | ||
42 | |||
43 | public AbstractTimelyMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
44 | super(mask, owner); | ||
45 | this.isLazy = isLazy; | ||
46 | this.memoryMap = CollectionsFactory.createMap(); | ||
47 | this.foldingStates = this.isLazy ? CollectionsFactory.createTreeMap() : null; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
52 | final Iterable<Tuple> signatures = other.getSignatures(); | ||
53 | for (final Tuple signature : signatures) { | ||
54 | if (other.isTimely()) { | ||
55 | final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature); | ||
56 | for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) { | ||
57 | for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) { | ||
58 | if (signed.getDirection() == Direction.DELETE) { | ||
59 | this.removeWithTimestamp(entry.getKey(), signed.getPayload()); | ||
60 | } else { | ||
61 | this.addWithTimestamp(entry.getKey(), signed.getPayload()); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | } else { | ||
66 | final Collection<Tuple> tuples = other.get(signature); | ||
67 | for (final Tuple tuple : tuples) { | ||
68 | this.addWithTimestamp(tuple, defaultValue); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | public boolean isPresentAtInfinityInteral(KeyType key) { | ||
75 | final TimelyMemory<Timestamp> values = this.memoryMap.get(key); | ||
76 | if (values == null) { | ||
77 | return false; | ||
78 | } else { | ||
79 | return values.getCountAtInfinity() != 0; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void clear() { | ||
85 | this.memoryMap.clear(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public int getKeysetSize() { | ||
90 | return this.memoryMap.keySet().size(); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public int getTotalSize() { | ||
95 | int i = 0; | ||
96 | for (final Entry<KeyType, TimelyMemory<Timestamp>> entry : this.memoryMap.entrySet()) { | ||
97 | i += entry.getValue().size(); | ||
98 | } | ||
99 | return i; | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public Iterator<Tuple> iterator() { | ||
104 | return this.memoryMap.values().stream().flatMap(e -> e.keySet().stream()).iterator(); | ||
105 | } | ||
106 | |||
107 | protected Collection<Tuple> getInternal(final KeyType key) { | ||
108 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(key); | ||
109 | if (memory == null) { | ||
110 | return null; | ||
111 | } else { | ||
112 | return memory.getTuplesAtInfinity(); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | public Map<Tuple, Timeline<Timestamp>> getWithTimestampInternal(final KeyType key) { | ||
117 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(key); | ||
118 | if (memory == null) { | ||
119 | return null; | ||
120 | } else { | ||
121 | return memory.asMap(); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | protected Diff<Timestamp> removeInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { | ||
126 | Timestamp oldResumableTimestamp = null; | ||
127 | Timestamp newResumableTimestamp = null; | ||
128 | |||
129 | final TimelyMemory<Timestamp> keyMemory = this.memoryMap.get(key); | ||
130 | if (keyMemory == null) { | ||
131 | throw raiseDuplicateDeletion(tuple); | ||
132 | } | ||
133 | |||
134 | if (this.isLazy) { | ||
135 | oldResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
136 | } | ||
137 | |||
138 | Diff<Timestamp> diff = null; | ||
139 | try { | ||
140 | diff = keyMemory.remove(tuple, timestamp); | ||
141 | } catch (final IllegalStateException e) { | ||
142 | throw raiseDuplicateDeletion(tuple); | ||
143 | } | ||
144 | if (keyMemory.isEmpty()) { | ||
145 | this.memoryMap.remove(key); | ||
146 | } | ||
147 | |||
148 | if (this.isLazy) { | ||
149 | newResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
150 | if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { | ||
151 | unregisterFoldingState(oldResumableTimestamp, key); | ||
152 | registerFoldingState(newResumableTimestamp, key); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return diff; | ||
157 | } | ||
158 | |||
159 | protected Diff<Timestamp> addInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { | ||
160 | Timestamp oldResumableTimestamp = null; | ||
161 | Timestamp newResumableTimestamp = null; | ||
162 | |||
163 | final TimelyMemory<Timestamp> keyMemory = this.memoryMap.computeIfAbsent(key, | ||
164 | k -> new TimelyMemory<Timestamp>(this.isLazy)); | ||
165 | |||
166 | if (this.isLazy) { | ||
167 | oldResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
168 | } | ||
169 | |||
170 | final Diff<Timestamp> diff = keyMemory.put(tuple, timestamp); | ||
171 | |||
172 | if (this.isLazy) { | ||
173 | newResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
174 | if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { | ||
175 | unregisterFoldingState(oldResumableTimestamp, key); | ||
176 | registerFoldingState(newResumableTimestamp, key); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | return diff; | ||
181 | } | ||
182 | |||
183 | @Override | ||
184 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
185 | return removeWithTimestamp(tuple, null, timestamp); | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
190 | return addWithTimestamp(tuple, null, timestamp); | ||
191 | } | ||
192 | |||
193 | @Override | ||
194 | public boolean isTimely() { | ||
195 | return true; | ||
196 | } | ||
197 | |||
198 | protected void registerFoldingState(final Timestamp timestamp, final KeyType key) { | ||
199 | if (timestamp != null) { | ||
200 | this.foldingStates.compute(timestamp, (k, v) -> { | ||
201 | if (v == null) { | ||
202 | v = CollectionsFactory.createSet(); | ||
203 | } | ||
204 | v.add(key); | ||
205 | return v; | ||
206 | }); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | protected void unregisterFoldingState(final Timestamp timestamp, final KeyType key) { | ||
211 | if (timestamp != null) { | ||
212 | this.foldingStates.compute(timestamp, (k, v) -> { | ||
213 | v.remove(key); | ||
214 | return v.isEmpty() ? null : v; | ||
215 | }); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | @Override | ||
220 | public Timestamp getResumableTimestamp() { | ||
221 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
222 | return null; | ||
223 | } else { | ||
224 | return this.foldingStates.firstKey(); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java new file mode 100644 index 00000000..ca06685a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java | |||
@@ -0,0 +1,100 @@ | |||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
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.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | |||
25 | /** | ||
26 | * Common parts of timely nullary and timely identity implementations. | ||
27 | * | ||
28 | * @noextend This class is not intended to be subclassed by clients. | ||
29 | * @author Tamas Szabo | ||
30 | * @since 2.3 | ||
31 | */ | ||
32 | abstract class AbstractTimelyTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
33 | |||
34 | protected final TimelyMemory<Timestamp> memory; | ||
35 | |||
36 | protected AbstractTimelyTrivialMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
37 | super(mask, owner); | ||
38 | this.memory = new TimelyMemory<Timestamp>(isLazy); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
43 | final Iterable<Tuple> signatures = other.getSignatures(); | ||
44 | for (final Tuple signature : signatures) { | ||
45 | if (other.isTimely()) { | ||
46 | final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature); | ||
47 | for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) { | ||
48 | for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) { | ||
49 | if (signed.getDirection() == Direction.DELETE) { | ||
50 | this.removeWithTimestamp(entry.getKey(), signed.getPayload()); | ||
51 | } else { | ||
52 | this.addWithTimestamp(entry.getKey(), signed.getPayload()); | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } else { | ||
57 | final Collection<Tuple> tuples = other.get(signature); | ||
58 | for (final Tuple tuple : tuples) { | ||
59 | this.removeWithTimestamp(tuple, defaultValue); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public void clear() { | ||
67 | this.memory.clear(); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public int getTotalSize() { | ||
72 | return this.memory.size(); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Iterator<Tuple> iterator() { | ||
77 | return this.memory.keySet().iterator(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
82 | return removeWithTimestamp(tuple, null, timestamp); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
87 | return addWithTimestamp(tuple, null, timestamp); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public boolean isTimely() { | ||
92 | return true; | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Timestamp getResumableTimestamp() { | ||
97 | return this.memory.getResumableTimestamp(); | ||
98 | } | ||
99 | |||
100 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java new file mode 100644 index 00000000..623d7399 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.memories.timely; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
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.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | |||
25 | /** | ||
26 | * Default timely implementation that covers all cases. | ||
27 | * | ||
28 | * @author Tamas Szabo | ||
29 | * @since 2.3 | ||
30 | */ | ||
31 | public final class TimelyDefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
32 | extends AbstractTimelyMaskedMemory<Timestamp, Tuple> { | ||
33 | |||
34 | public TimelyDefaultMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
35 | super(mask, owner, isLazy); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Iterable<Tuple> getSignatures() { | ||
40 | return this.memoryMap.keySet(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, | ||
45 | final Timestamp timestamp) { | ||
46 | final Tuple key = mask.transform(tuple); | ||
47 | return removeInternal(key, tuple, timestamp); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, | ||
52 | final Timestamp timestamp) { | ||
53 | final Tuple key = this.mask.transform(tuple); | ||
54 | return addInternal(key, tuple, timestamp); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public Collection<Tuple> get(final ITuple signature) { | ||
59 | return getInternal(signature.toImmutable()); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
64 | return getWithTimestampInternal(signature.toImmutable()); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
69 | return isPresentAtInfinityInteral(signature.toImmutable()); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Set<Tuple> getResumableSignatures() { | ||
74 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
75 | return Collections.emptySet(); | ||
76 | } else { | ||
77 | return this.foldingStates.firstEntry().getValue(); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
83 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
84 | final Timestamp resumableTimestamp = this.getResumableTimestamp(); | ||
85 | if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { | ||
86 | throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); | ||
87 | } | ||
88 | final Set<Tuple> signatures = this.foldingStates.remove(timestamp); | ||
89 | for (final Tuple signature : signatures) { | ||
90 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature); | ||
91 | final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp); | ||
92 | result.put(signature, diffMap); | ||
93 | registerFoldingState(memory.getResumableTimestamp(), signature); | ||
94 | } | ||
95 | return result; | ||
96 | } | ||
97 | |||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java new file mode 100644 index 00000000..568f274d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java | |||
@@ -0,0 +1,106 @@ | |||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | |||
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.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
23 | |||
24 | /** | ||
25 | * Timely specialization for identity mask. | ||
26 | * | ||
27 | * @author Tamas Szabo | ||
28 | * @since 2.3 | ||
29 | */ | ||
30 | public final class TimelyIdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
31 | extends AbstractTimelyTrivialMaskedMemory<Timestamp> { | ||
32 | |||
33 | public TimelyIdentityMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
34 | super(mask, owner, isLazy); | ||
35 | if (!mask.isIdentity()) | ||
36 | throw new IllegalArgumentException(mask.toString()); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public int getKeysetSize() { | ||
41 | return this.memory.size(); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return this.memory.keySet(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Collection<Tuple> get(final ITuple signature) { | ||
51 | if (this.memory.getTuplesAtInfinity().contains(signature)) { | ||
52 | return Collections.singleton(signature.toImmutable()); | ||
53 | } else { | ||
54 | return null; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
60 | final Timeline<Timestamp> value = this.memory.get(signature); | ||
61 | if (value != null) { | ||
62 | return Collections.singletonMap(signature.toImmutable(), value); | ||
63 | } else { | ||
64 | return null; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
70 | try { | ||
71 | return this.memory.remove(tuple, timestamp); | ||
72 | } catch (final IllegalStateException e) { | ||
73 | throw raiseDuplicateDeletion(tuple); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
79 | return this.memory.put(tuple, timestamp); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
84 | return this.memory.isPresentAtInfinity(signature.toImmutable()); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public Set<Tuple> getResumableSignatures() { | ||
89 | if (this.memory.getResumableTimestamp() != null) { | ||
90 | return this.memory.getResumableTuples(); | ||
91 | } else { | ||
92 | return Collections.emptySet(); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
98 | final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp); | ||
99 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
100 | for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) { | ||
101 | result.put(entry.getKey(), Collections.singletonMap(entry.getKey(), entry.getValue())); | ||
102 | } | ||
103 | return result; | ||
104 | } | ||
105 | |||
106 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java new file mode 100644 index 00000000..75987a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java | |||
@@ -0,0 +1,108 @@ | |||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
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.timeline.Diff; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
22 | |||
23 | /** | ||
24 | * Timely specialization for nullary mask. | ||
25 | * | ||
26 | * @author Tamas Szabo | ||
27 | * @since 2.3 | ||
28 | */ | ||
29 | public final class TimelyNullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
30 | extends AbstractTimelyTrivialMaskedMemory<Timestamp> { | ||
31 | |||
32 | protected static final Tuple EMPTY_TUPLE = Tuples.staticArityFlatTupleOf(); | ||
33 | protected static final Set<Tuple> UNIT_RELATION = Collections.singleton(EMPTY_TUPLE); | ||
34 | protected static final Set<Tuple> EMPTY_RELATION = Collections.emptySet(); | ||
35 | |||
36 | public TimelyNullaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
37 | super(mask, owner, isLazy); | ||
38 | if (0 != mask.getSize()) { | ||
39 | throw new IllegalArgumentException(mask.toString()); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public int getKeysetSize() { | ||
45 | return this.memory.isEmpty() ? 0 : 1; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Iterable<Tuple> getSignatures() { | ||
50 | return this.memory.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Collection<Tuple> get(final ITuple signature) { | ||
55 | if (0 == signature.getSize()) { | ||
56 | return this.memory.getTuplesAtInfinity(); | ||
57 | } else { | ||
58 | return null; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
64 | if (0 == signature.getSize()) { | ||
65 | return this.memory.asMap(); | ||
66 | } else { | ||
67 | return null; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
73 | try { | ||
74 | return this.memory.remove(tuple, timestamp); | ||
75 | } catch (final IllegalStateException e) { | ||
76 | throw raiseDuplicateDeletion(tuple); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
82 | return this.memory.put(tuple, timestamp); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
87 | if (0 == signature.getSize()) { | ||
88 | return this.memory.getCountAtInfinity() > 0; | ||
89 | } else { | ||
90 | return false; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public Set<Tuple> getResumableSignatures() { | ||
96 | if (this.memory.getResumableTimestamp() != null) { | ||
97 | return UNIT_RELATION; | ||
98 | } else { | ||
99 | return EMPTY_RELATION; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
105 | return Collections.singletonMap(EMPTY_TUPLE, this.memory.resumeAt(timestamp)); | ||
106 | } | ||
107 | |||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java new file mode 100644 index 00000000..178193af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java | |||
@@ -0,0 +1,133 @@ | |||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
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.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * Timely specialization for unary mask. | ||
28 | * | ||
29 | * @author Tamas Szabo | ||
30 | * @since 2.3 | ||
31 | */ | ||
32 | public final class TimelyUnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
33 | extends AbstractTimelyMaskedMemory<Timestamp, Object> { | ||
34 | |||
35 | protected final int keyPosition; | ||
36 | |||
37 | public TimelyUnaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
38 | super(mask, owner, isLazy); | ||
39 | if (1 != mask.getSize()) | ||
40 | throw new IllegalArgumentException(mask.toString()); | ||
41 | this.keyPosition = mask.indices[0]; | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return () -> { | ||
47 | final Iterator<Object> wrapped = this.memoryMap.keySet().iterator(); | ||
48 | return new Iterator<Tuple>() { | ||
49 | @Override | ||
50 | public boolean hasNext() { | ||
51 | return wrapped.hasNext(); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Tuple next() { | ||
56 | final Object key = wrapped.next(); | ||
57 | return Tuples.staticArityFlatTupleOf(key); | ||
58 | } | ||
59 | }; | ||
60 | }; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
65 | final Object key = tuple.get(keyPosition); | ||
66 | return removeInternal(key, tuple, timestamp); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
71 | final Object key = tuple.get(keyPosition); | ||
72 | return addInternal(key, tuple, timestamp); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Collection<Tuple> get(final ITuple signature) { | ||
77 | return getInternal(signature.get(0)); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
82 | return getWithTimestampInternal(signature.get(0)); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean isPresentAtInfinity(ITuple signature) { | ||
87 | return isPresentAtInfinityInteral(signature.get(0)); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public Iterable<Tuple> getResumableSignatures() { | ||
92 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
93 | return Collections.emptySet(); | ||
94 | } else { | ||
95 | return () -> { | ||
96 | final Iterator<Object> wrapped = this.foldingStates.firstEntry().getValue().iterator(); | ||
97 | return new Iterator<Tuple>() { | ||
98 | @Override | ||
99 | public boolean hasNext() { | ||
100 | return wrapped.hasNext(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Tuple next() { | ||
105 | final Object key = wrapped.next(); | ||
106 | return Tuples.staticArityFlatTupleOf(key); | ||
107 | } | ||
108 | }; | ||
109 | }; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
115 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
116 | final Timestamp resumableTimestamp = this.getResumableTimestamp(); | ||
117 | if (resumableTimestamp == null) { | ||
118 | throw new IllegalStateException("There is nothing to fold!"); | ||
119 | } else if (resumableTimestamp.compareTo(timestamp) != 0) { | ||
120 | throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); | ||
121 | } | ||
122 | |||
123 | final Set<Object> signatures = this.foldingStates.remove(timestamp); | ||
124 | for (final Object signature : signatures) { | ||
125 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature); | ||
126 | final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp); | ||
127 | result.put(Tuples.staticArityFlatTupleOf(signature), diffMap); | ||
128 | registerFoldingState(memory.getResumableTimestamp(), signature); | ||
129 | } | ||
130 | return result; | ||
131 | } | ||
132 | |||
133 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java new file mode 100644 index 00000000..c9f4b305 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.planning; | ||
11 | |||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | |||
20 | /** | ||
21 | * | ||
22 | * An implicit common parameter is the "effort" PatternDescription. This | ||
23 | * indicates that the build request is part of an effort to build the matcher of | ||
24 | * the given pattern; it it important to record this during code generation so | ||
25 | * that the generated code can be separated according to patterns. | ||
26 | * | ||
27 | * @param <Collector> | ||
28 | * the handle of a receiver-like RETE ending to which plans can be | ||
29 | * connected | ||
30 | * @author Gabor Bergmann | ||
31 | * @noimplement This interface is not intended to be implemented by clients. | ||
32 | */ | ||
33 | public interface IOperationCompiler<Collector> { | ||
34 | |||
35 | /** | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | */ | ||
38 | public Collector patternCollector(PQuery pattern); | ||
39 | |||
40 | public void buildConnection(SubPlan parentPlan, Collector collector); | ||
41 | |||
42 | /** | ||
43 | * @since 0.9 | ||
44 | */ | ||
45 | public void patternFinished(PQuery pattern, Collector collector); | ||
46 | |||
47 | /** | ||
48 | * @throws ViatraQueryRuntimeException | ||
49 | */ | ||
50 | public SubPlan patternCallPlan(Tuple nodes, PQuery supplierKey); | ||
51 | |||
52 | public SubPlan transitiveInstantiationPlan(Tuple nodes); | ||
53 | |||
54 | public SubPlan directInstantiationPlan(Tuple nodes); | ||
55 | |||
56 | public SubPlan transitiveGeneralizationPlan(Tuple nodes); | ||
57 | |||
58 | public SubPlan directGeneralizationPlan(Tuple nodes); | ||
59 | |||
60 | public SubPlan transitiveContainmentPlan(Tuple nodes); | ||
61 | |||
62 | public SubPlan directContainmentPlan(Tuple nodes); | ||
63 | |||
64 | public SubPlan binaryEdgeTypePlan(Tuple nodes, Object supplierKey); | ||
65 | |||
66 | public SubPlan ternaryEdgeTypePlan(Tuple nodes, Object supplierKey); | ||
67 | |||
68 | public SubPlan unaryTypePlan(Tuple nodes, Object supplierKey); | ||
69 | |||
70 | public SubPlan buildStartingPlan(Object[] constantValues, Object[] constantNames); | ||
71 | |||
72 | public SubPlan buildEqualityChecker(SubPlan parentPlan, int[] indices); | ||
73 | |||
74 | public SubPlan buildInjectivityChecker(SubPlan parentPlan, int subject, int[] inequalIndices); | ||
75 | |||
76 | public SubPlan buildTransitiveClosure(SubPlan parentPlan); | ||
77 | |||
78 | public SubPlan buildTrimmer(SubPlan parentPlan, TupleMask trimMask, boolean enforceUniqueness); | ||
79 | |||
80 | public SubPlan buildBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
81 | TupleMask primaryMask, TupleMask sideMask, TupleMask complementer, boolean negative); | ||
82 | |||
83 | public SubPlan buildCounterBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
84 | TupleMask primaryMask, TupleMask originalSideMask, TupleMask complementer, | ||
85 | Object aggregateResultCalibrationElement); | ||
86 | |||
87 | public SubPlan buildCountCheckBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
88 | TupleMask primaryMask, TupleMask originalSideMask, int resultPositionInSignature); | ||
89 | |||
90 | public SubPlan buildPredicateChecker(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap, | ||
91 | SubPlan parentPlan); | ||
92 | public SubPlan buildFunctionEvaluator(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap, | ||
93 | SubPlan parentPlan, Object computedResultCalibrationElement); | ||
94 | |||
95 | /** | ||
96 | * @return an operation compiler that potentially acts on a separate container | ||
97 | */ | ||
98 | public IOperationCompiler<Collector> getNextContainer(); | ||
99 | |||
100 | /** | ||
101 | * @return an operation compiler that puts build actions on the tab of the given pattern | ||
102 | * @since 0.9 | ||
103 | */ | ||
104 | public IOperationCompiler<Collector> putOnTab(PQuery effort /*, IPatternMatcherContext context*/); | ||
105 | |||
106 | public void reinitialize(); | ||
107 | |||
108 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java new file mode 100644 index 00000000..6ce9d91b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.planning; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
16 | |||
17 | /** | ||
18 | * An algorithm that builds a query plan based on a PSystem representation of a body of constraints. This interface is | ||
19 | * for internal use of the various query backends. | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | */ | ||
23 | public interface IQueryPlannerStrategy { | ||
24 | |||
25 | /** | ||
26 | * @throws ViatraQueryRuntimeException | ||
27 | */ | ||
28 | public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context); | ||
29 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java new file mode 100644 index 00000000..501ddf73 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java | |||
@@ -0,0 +1,102 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | |||
13 | /** | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 0.9 | ||
16 | */ | ||
17 | public class QueryProcessingException extends ViatraQueryRuntimeException { | ||
18 | |||
19 | private static final long serialVersionUID = -8272290113656867086L; | ||
20 | /** | ||
21 | * Binding the '{n}' (n = 1..N) strings to contextual conditions in 'context' | ||
22 | * | ||
23 | * @param context | ||
24 | * : array of context-sensitive Strings | ||
25 | */ | ||
26 | protected static String bind(String message, String[] context) { | ||
27 | if (context == null) | ||
28 | return message; | ||
29 | |||
30 | String internal = message; | ||
31 | for (int i = 0; i < context.length; i++) { | ||
32 | internal = internal.replace("{" + (i + 1) + "}", context[i] != null ? context[i] : "<<null>>"); | ||
33 | } | ||
34 | return internal; | ||
35 | } | ||
36 | |||
37 | private Object patternDescription; | ||
38 | private String shortMessage; | ||
39 | |||
40 | /** | ||
41 | * @param message | ||
42 | * The template of the exception message | ||
43 | * @param context | ||
44 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
45 | * defined | ||
46 | * @param patternDescription | ||
47 | * the PatternDescription where the exception occurred | ||
48 | * @since 2.0 | ||
49 | */ | ||
50 | public QueryProcessingException(String message, Object patternDescription) { | ||
51 | super(message); | ||
52 | initializeFields(message, patternDescription); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @param message | ||
57 | * The template of the exception message | ||
58 | * @param context | ||
59 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
60 | * defined | ||
61 | * @param patternDescription | ||
62 | * the PatternDescription where the exception occurred | ||
63 | */ | ||
64 | public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
65 | super(bind(message, context)); | ||
66 | initializeFields(shortMessage, patternDescription); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * @param message | ||
71 | * The template of the exception message | ||
72 | * @param context | ||
73 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
74 | * defined | ||
75 | * @param patternDescription | ||
76 | * the PatternDescription where the exception occurred | ||
77 | */ | ||
78 | public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription, | ||
79 | Throwable cause) { | ||
80 | super(bind(message, context), cause); | ||
81 | initializeFields(shortMessage, patternDescription); | ||
82 | } | ||
83 | |||
84 | public Object getPatternDescription() { | ||
85 | return patternDescription; | ||
86 | } | ||
87 | |||
88 | public String getShortMessage() { | ||
89 | return shortMessage; | ||
90 | } | ||
91 | |||
92 | private void initializeFields(String shortMessage, Object patternDescription) { | ||
93 | this.patternDescription = patternDescription; | ||
94 | this.shortMessage = shortMessage; | ||
95 | } | ||
96 | |||
97 | |||
98 | public void setPatternDescription(Object patternDescription) { | ||
99 | this.patternDescription = patternDescription; | ||
100 | } | ||
101 | |||
102 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java new file mode 100644 index 00000000..1998df9d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java | |||
@@ -0,0 +1,240 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.planning; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | import java.util.WeakHashMap; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
28 | |||
29 | /** | ||
30 | * A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for | ||
31 | * representing a state of the plan; but after it is initialized it is expected be immutable | ||
32 | * (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}). | ||
33 | * | ||
34 | * <p> A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
35 | * Important maintained information: <ul> | ||
36 | * <li>set of <b>variables</b> whose values are known when the runtime evaluation is at this stage, | ||
37 | * <li>set of <b>constraints</b> that are known to hold true at this point. | ||
38 | * </ul> | ||
39 | * | ||
40 | * <p> Recommended to instantiate via a {@link SubPlanFactory} or subclasses, | ||
41 | * so that query planners can subclass SubPlan if needed. | ||
42 | * | ||
43 | * @author Gabor Bergmann | ||
44 | * | ||
45 | */ | ||
46 | public class SubPlan { | ||
47 | private PBody body; | ||
48 | private List<? extends SubPlan> parentPlans; | ||
49 | private POperation operation; | ||
50 | |||
51 | private final Set<PVariable> visibleVariables; | ||
52 | private final Set<PVariable> allVariables; | ||
53 | private final Set<PVariable> introducedVariables; // delta compared to first parent | ||
54 | private Set<PConstraint> allConstraints; | ||
55 | private Set<PConstraint> deltaConstraints; // delta compared to all parents | ||
56 | private Set<PConstraint> externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents | ||
57 | |||
58 | |||
59 | |||
60 | |||
61 | |||
62 | /** | ||
63 | * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
64 | */ | ||
65 | public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) { | ||
66 | this(body, operation, Arrays.asList(parentPlans)); | ||
67 | } | ||
68 | /** | ||
69 | * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
70 | */ | ||
71 | public SubPlan(PBody body, POperation operation, List<? extends SubPlan> parentPlans) { | ||
72 | super(); | ||
73 | this.body = body; | ||
74 | this.parentPlans = parentPlans; | ||
75 | this.operation = operation; | ||
76 | |||
77 | this.externallyInferredConstraints = new HashSet<PConstraint>(); | ||
78 | this.deltaConstraints = new HashSet<PConstraint>(operation.getDeltaConstraints()); | ||
79 | |||
80 | this.allVariables = new HashSet<PVariable>(); | ||
81 | for (PConstraint constraint: deltaConstraints) { | ||
82 | this.allVariables.addAll(constraint.getDeducedVariables()); | ||
83 | } | ||
84 | this.allConstraints = new HashSet<PConstraint>(deltaConstraints); | ||
85 | for (SubPlan parentPlan: parentPlans) { | ||
86 | this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints()); | ||
87 | this.allVariables.addAll(parentPlan.getAllDeducedVariables()); | ||
88 | } | ||
89 | |||
90 | // TODO this is ugly a bit | ||
91 | if (operation instanceof PStart) { | ||
92 | this.visibleVariables = new HashSet<PVariable>(((PStart) operation).getAPrioriVariables()); | ||
93 | this.allVariables.addAll(visibleVariables); | ||
94 | } else if (operation instanceof PProject) { | ||
95 | this.visibleVariables = new HashSet<PVariable>(((PProject) operation).getToVariables()); | ||
96 | } else { | ||
97 | this.visibleVariables = new HashSet<PVariable>(); | ||
98 | for (SubPlan parentPlan: parentPlans) | ||
99 | this.visibleVariables.addAll(parentPlan.getVisibleVariables()); | ||
100 | for (PConstraint constraint: deltaConstraints) | ||
101 | this.visibleVariables.addAll(constraint.getDeducedVariables()); | ||
102 | } | ||
103 | |||
104 | this.introducedVariables = new HashSet<PVariable>(this.visibleVariables); | ||
105 | if (!parentPlans.isEmpty()) | ||
106 | introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables()); | ||
107 | |||
108 | operation.checkConsistency(this); | ||
109 | } | ||
110 | |||
111 | |||
112 | @Override | ||
113 | public String toString() { | ||
114 | return toLongString(); | ||
115 | } | ||
116 | public String toShortString() { | ||
117 | return String.format("Plan{%s}:%s", | ||
118 | visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")), | ||
119 | operation.getShortName()); | ||
120 | } | ||
121 | public String toLongString() { | ||
122 | return String.format("%s<%s>", | ||
123 | toShortString(), | ||
124 | parentPlans.stream().map(Object::toString).collect(Collectors.joining("; "))); | ||
125 | } | ||
126 | |||
127 | |||
128 | /** | ||
129 | * All constraints that are known to hold at this point | ||
130 | */ | ||
131 | public Set<PConstraint> getAllEnforcedConstraints() { | ||
132 | return allConstraints; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * The new constraints enforced at this stage of plan, that aren't yet enforced at parents | ||
137 | * (results are also included in {@link SubPlan#getAllEnforcedConstraints()}) | ||
138 | */ | ||
139 | public Set<PConstraint> getDeltaEnforcedConstraints() { | ||
140 | return deltaConstraints; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Indicate that a given constraint was found to be automatically satisfied at this point | ||
145 | * without additional operations. | ||
146 | * (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()}) | ||
147 | * | ||
148 | * <p>Warning: not propagated automatically to child plans, | ||
149 | * so best to invoke before constructing further SubPlans. </p> | ||
150 | */ | ||
151 | public void inferConstraint(PConstraint constraint) { | ||
152 | externallyInferredConstraints.add(constraint); | ||
153 | deltaConstraints.add(constraint); | ||
154 | allConstraints.add(constraint); | ||
155 | } | ||
156 | |||
157 | public PBody getBody() { | ||
158 | return body; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * Variables which are assigned a value at this point | ||
163 | * (results are also included in {@link SubPlan#getAllDeducedVariables()}) | ||
164 | */ | ||
165 | public Set<PVariable> getVisibleVariables() { | ||
166 | return visibleVariables; | ||
167 | } | ||
168 | /** | ||
169 | * Variables which have been assigned a value; | ||
170 | * includes visible variables (see {@link #getVisibleVariables()}) | ||
171 | * and additionally any variables hidden by a projection (see {@link PProject}). | ||
172 | */ | ||
173 | public Set<PVariable> getAllDeducedVariables() { | ||
174 | return allVariables; | ||
175 | } | ||
176 | /** | ||
177 | * Delta compared to first parent: variables that are visible here but were not visible at the first parent. | ||
178 | */ | ||
179 | public Set<PVariable> getIntroducedVariables() { | ||
180 | return introducedVariables; | ||
181 | } | ||
182 | public List<? extends SubPlan> getParentPlans() { | ||
183 | return parentPlans; | ||
184 | } | ||
185 | public POperation getOperation() { | ||
186 | return operation; | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * The closure of all type judgments of enforced constraints at this point. | ||
192 | * <p> No subsumption applied. | ||
193 | */ | ||
194 | public Set<TypeJudgement> getAllImpliedTypeJudgements(IQueryMetaContext context) { | ||
195 | Set<TypeJudgement> impliedJudgements = allImpliedTypeJudgements.get(context); | ||
196 | if (impliedJudgements == null) { | ||
197 | Set<TypeJudgement> equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context); | ||
198 | impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context); | ||
199 | |||
200 | allImpliedTypeJudgements.put(context, impliedJudgements); | ||
201 | } | ||
202 | return impliedJudgements; | ||
203 | } | ||
204 | private WeakHashMap<IQueryMetaContext, Set<TypeJudgement>> allImpliedTypeJudgements = new WeakHashMap<IQueryMetaContext, Set<TypeJudgement>>(); | ||
205 | |||
206 | |||
207 | @Override | ||
208 | public int hashCode() { | ||
209 | final int prime = 31; | ||
210 | int result = 1; | ||
211 | result = prime * result | ||
212 | + ((operation == null) ? 0 : operation.hashCode()); | ||
213 | result = prime * result | ||
214 | + ((parentPlans == null) ? 0 : parentPlans.hashCode()); | ||
215 | return result; | ||
216 | } | ||
217 | @Override | ||
218 | public boolean equals(Object obj) { | ||
219 | if (this == obj) | ||
220 | return true; | ||
221 | if (obj == null) | ||
222 | return false; | ||
223 | if (!(obj instanceof SubPlan)) | ||
224 | return false; | ||
225 | SubPlan other = (SubPlan) obj; | ||
226 | if (operation == null) { | ||
227 | if (other.operation != null) | ||
228 | return false; | ||
229 | } else if (!operation.equals(other.operation)) | ||
230 | return false; | ||
231 | if (parentPlans == null) { | ||
232 | if (other.parentPlans != null) | ||
233 | return false; | ||
234 | } else if (!parentPlans.equals(other.parentPlans)) | ||
235 | return false; | ||
236 | return true; | ||
237 | } | ||
238 | |||
239 | |||
240 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java new file mode 100644 index 00000000..d0df5fac --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
13 | |||
14 | /** | ||
15 | * Single entry point for creating subplans. | ||
16 | * Can be subclassed by query planner to provide specialized SubPlans. | ||
17 | * @author Bergmann Gabor | ||
18 | * | ||
19 | */ | ||
20 | public class SubPlanFactory { | ||
21 | |||
22 | protected PBody body; | ||
23 | |||
24 | public SubPlanFactory(PBody body) { | ||
25 | super(); | ||
26 | this.body = body; | ||
27 | } | ||
28 | |||
29 | public SubPlan createSubPlan(POperation operation, SubPlan... parentPlans) { | ||
30 | return new SubPlan(body, operation, parentPlans); | ||
31 | } | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java new file mode 100644 index 00000000..ed5d1cbb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java | |||
@@ -0,0 +1,165 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.planning.helpers; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * | ||
32 | */ | ||
33 | public class BuildHelper { | ||
34 | |||
35 | private BuildHelper() { | ||
36 | // Hiding constructor for utility class | ||
37 | } | ||
38 | |||
39 | // public static SubPlan naturalJoin(IOperationCompiler buildable, | ||
40 | // SubPlan primaryPlan, SubPlan secondaryPlan) { | ||
41 | // JoinHelper joinHelper = new JoinHelper(primaryPlan, secondaryPlan); | ||
42 | // return buildable.buildBetaNode(primaryPlan, secondaryPlan, joinHelper.getPrimaryMask(), | ||
43 | // joinHelper.getSecondaryMask(), joinHelper.getComplementerMask(), false); | ||
44 | // } | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Reduces the number of tuples by trimming (existentially quantifying) the set of variables that <ul> | ||
49 | * <li> are visible in the subplan, | ||
50 | * <li> are not exported parameters, | ||
51 | * <li> have all their constraints already enforced in the subplan, | ||
52 | * </ul> and thus will not be needed anymore. | ||
53 | * | ||
54 | * @param onlyIfNotDetermined if true, no trimming performed unless there is at least one variable that is not functionally determined | ||
55 | * @return the plan after the trimming (possibly the original) | ||
56 | * @since 1.5 | ||
57 | */ | ||
58 | public static SubPlan trimUnneccessaryVariables(SubPlanFactory planFactory, /*IOperationCompiler buildable,*/ | ||
59 | SubPlan plan, boolean onlyIfNotDetermined, QueryAnalyzer analyzer) { | ||
60 | Set<PVariable> canBeTrimmed = new HashSet<PVariable>(); | ||
61 | Set<PVariable> variablesInPlan = plan.getVisibleVariables(); | ||
62 | for (PVariable trimCandidate : variablesInPlan) { | ||
63 | if (trimCandidate.getReferringConstraintsOfType(ExportedParameter.class).isEmpty()) { | ||
64 | if (plan.getAllEnforcedConstraints().containsAll(trimCandidate.getReferringConstraints())) | ||
65 | canBeTrimmed.add(trimCandidate); | ||
66 | } | ||
67 | } | ||
68 | final Set<PVariable> retainedVars = setMinus(variablesInPlan, canBeTrimmed); | ||
69 | if (!canBeTrimmed.isEmpty() && !(onlyIfNotDetermined && areVariablesDetermined(plan, retainedVars, canBeTrimmed, analyzer, false))) { | ||
70 | // TODO add smart ordering? | ||
71 | plan = planFactory.createSubPlan(new PProject(retainedVars), plan); | ||
72 | } | ||
73 | return plan; | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * @return true iff a set of given variables functionally determine all visible variables in the subplan according to the subplan's constraints | ||
78 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
79 | * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
80 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
81 | * @since 1.5 | ||
82 | */ | ||
83 | public static boolean areAllVariablesDetermined(SubPlan plan, Collection<PVariable> determining, QueryAnalyzer analyzer, boolean strict) { | ||
84 | return areVariablesDetermined(plan, determining, plan.getVisibleVariables(), analyzer, strict); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * @return true iff one set of given variables functionally determine the other set according to the subplan's constraints | ||
89 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
90 | * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
91 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
92 | * @since 1.5 | ||
93 | */ | ||
94 | public static boolean areVariablesDetermined(SubPlan plan, Collection<PVariable> determining, Collection<PVariable> determined, | ||
95 | QueryAnalyzer analyzer, boolean strict) { | ||
96 | Map<Set<PVariable>, Set<PVariable>> dependencies = analyzer.getFunctionalDependencies(plan.getAllEnforcedConstraints(), strict); | ||
97 | final Set<PVariable> closure = FunctionalDependencyHelper.closureOf(determining, dependencies); | ||
98 | final boolean isDetermined = closure.containsAll(determined); | ||
99 | return isDetermined; | ||
100 | } | ||
101 | |||
102 | private static <T> Set<T> setMinus(Set<T> a, Set<T> b) { | ||
103 | Set<T> difference = new HashSet<T>(a); | ||
104 | difference.removeAll(b); | ||
105 | return difference; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Finds an arbitrary constraint that is not enforced at the given plan. | ||
110 | * | ||
111 | * @param pSystem | ||
112 | * @param plan | ||
113 | * @return a PConstraint that is not enforced, if any, or null if all are enforced | ||
114 | */ | ||
115 | public static PConstraint getAnyUnenforcedConstraint(PBody pSystem, | ||
116 | SubPlan plan) { | ||
117 | Set<PConstraint> allEnforcedConstraints = plan.getAllEnforcedConstraints(); | ||
118 | Set<PConstraint> constraints = pSystem.getConstraints(); | ||
119 | for (PConstraint pConstraint : constraints) { | ||
120 | if (!allEnforcedConstraints.contains(pConstraint)) | ||
121 | return pConstraint; | ||
122 | } | ||
123 | return null; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Skips the last few steps, if any, that are projections, so that a custom projection can be added instead. | ||
128 | * Useful for connecting body final plans into the production node. | ||
129 | * | ||
130 | * @since 2.1 | ||
131 | */ | ||
132 | public static SubPlan eliminateTrailingProjections(SubPlan plan) { | ||
133 | while (plan.getOperation() instanceof PProject) | ||
134 | plan = plan.getParentPlans().get(0); | ||
135 | return plan; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Verifies whether all constraints are enforced and exported parameters are present. | ||
140 | * | ||
141 | * @param pSystem | ||
142 | * @param plan | ||
143 | * @throws ViatraQueryRuntimeException | ||
144 | */ | ||
145 | public static void finalCheck(final PBody pSystem, SubPlan plan, IQueryMetaContext context) { | ||
146 | PConstraint unenforcedConstraint = getAnyUnenforcedConstraint(pSystem, plan); | ||
147 | if (unenforcedConstraint != null) { | ||
148 | throw new QueryProcessingException( | ||
149 | "Pattern matcher construction terminated without successfully enforcing constraint {1}." | ||
150 | + " Could be caused if the value of some variables can not be deduced, e.g. by circularity of pattern constraints.", | ||
151 | new String[] { unenforcedConstraint.toString() }, "Could not enforce a pattern constraint", null); | ||
152 | } | ||
153 | for (ExportedParameter export : pSystem | ||
154 | .getConstraintsOfType(ExportedParameter.class)) { | ||
155 | if (!export.isReadyAt(plan, context)) { | ||
156 | throw new QueryProcessingException( | ||
157 | "Exported pattern parameter {1} could not be deduced during pattern matcher construction." | ||
158 | + " A pattern constraint is required to positively deduce its value.", | ||
159 | new String[] { export.getParameterName() }, "Could not calculate pattern parameter", | ||
160 | null); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java new file mode 100644 index 00000000..40835f52 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java | |||
@@ -0,0 +1,143 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Adam Dudas, Istvan Rath and Daniel Varro | ||
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.planning.helpers; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Map.Entry; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
20 | |||
21 | /** | ||
22 | * Helper utility class for functional dependency analysis. | ||
23 | * | ||
24 | * Throughout this class attribute sets are represented as generic sets and functional dependencies as maps from | ||
25 | * attribute set (generic sets) to attribute set (generic sets) | ||
26 | * | ||
27 | * @author Adam Dudas | ||
28 | * | ||
29 | */ | ||
30 | public class FunctionalDependencyHelper { | ||
31 | |||
32 | private FunctionalDependencyHelper() { | ||
33 | // Hiding constructor for utility class | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Get the closure of the specified attribute set relative to the specified functional dependencies. | ||
38 | * | ||
39 | * @param attributes | ||
40 | * The attributes to get the closure of. | ||
41 | * @param dependencies | ||
42 | * The functional dependencies of which the closure operation is relative to. | ||
43 | * @return The closure of the specified attribute set relative to the specified functional dependencies. | ||
44 | */ | ||
45 | public static <A> Set<A> closureOf(Collection<A> attributes, Map<Set<A>, Set<A>> dependencies) { | ||
46 | Set<A> closureSet = new HashSet<A>(); | ||
47 | |||
48 | for (Set<A> closureSet1 = new HashSet<A>(attributes); closureSet.addAll(closureSet1);) { | ||
49 | closureSet1 = new HashSet<A>(); | ||
50 | for (Entry<Set<A>, Set<A>> dependency : dependencies.entrySet()) { | ||
51 | if (closureSet.containsAll(dependency.getKey())) | ||
52 | closureSet1.addAll(dependency.getValue()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | return closureSet; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @return true if the dependency from the left set to the right set is trivial | ||
61 | * @since 1.5 | ||
62 | */ | ||
63 | public static <A> boolean isTrivial(Set<A> left, Set<A> right) { | ||
64 | return left.containsAll(right); | ||
65 | } | ||
66 | |||
67 | /*** | ||
68 | * Returns the dependency set over attributes in {@link targetAttributes} that are implied by a given source dependency set. | ||
69 | * <p> Note: exponential in the size of the target attribute set. | ||
70 | * <p> Note: minimality of the returned dependency set is currently not guaranteed. | ||
71 | * @param originalDependencies all dependencies that are known to hold on a wider set of attributes | ||
72 | * @param targetAttributes the set of attributes we are interested in | ||
73 | * @since 1.5 | ||
74 | */ | ||
75 | public static <A> Map<Set<A>, Set<A>> projectDependencies(Map<Set<A>, Set<A>> originalDependencies, Set<A> targetAttributes) { | ||
76 | // only those attributes are considered as left-hand-side candidates that occur at least once in dependencies | ||
77 | Set<A> leftCandidates = new HashSet<A>(); | ||
78 | for (Entry<Set<A>, Set<A>> dependency : originalDependencies.entrySet()) { | ||
79 | if (!isTrivial(dependency.getKey(), dependency.getValue())) // only if non-trivial | ||
80 | leftCandidates.addAll(Sets.intersection(dependency.getKey(), targetAttributes)); | ||
81 | } | ||
82 | |||
83 | // Compute an initial list of nontrivial projected dependencies - it does not have to be minimal yet | ||
84 | Map<Set<A>, Set<A>> initialDependencies = new HashMap<Set<A>, Set<A>>(); | ||
85 | for (Set<A> leftSet : Sets.powerSet(leftCandidates)) { | ||
86 | Set<A> rightSet = Sets.intersection(closureOf(leftSet, originalDependencies), targetAttributes); | ||
87 | if (!isTrivial(leftSet, rightSet)) { | ||
88 | initialDependencies.put(leftSet, rightSet); | ||
89 | } | ||
90 | } | ||
91 | // Don't forget to include constants! | ||
92 | Set<A> constants = Sets.intersection(closureOf(Collections.<A>emptySet(), originalDependencies), targetAttributes); | ||
93 | if (! constants.isEmpty()) { | ||
94 | initialDependencies.put(Collections.<A>emptySet(), constants); | ||
95 | } | ||
96 | |||
97 | // Omit those dependencies where the LHS has superfluous attributes | ||
98 | Map<Set<A>, Set<A>> solidDependencies = new HashMap<Set<A>, Set<A>>(); | ||
99 | for (Entry<Set<A>, Set<A>> dependency : initialDependencies.entrySet()) { | ||
100 | Set<A> leftSet = dependency.getKey(); | ||
101 | Set<A> rightSet = dependency.getValue(); | ||
102 | boolean solid = true; | ||
103 | for (A skipped : leftSet) { // what if we skip one attribute from the left set? | ||
104 | Set<A> singleton = Collections.singleton(skipped); | ||
105 | Set<A> candidate = Sets.difference(leftSet, singleton); | ||
106 | Set<A> rightCandidate = initialDependencies.get(candidate); | ||
107 | if (rightCandidate != null) { | ||
108 | if (Sets.union(rightCandidate, singleton).containsAll(rightSet)) { | ||
109 | solid = false; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | if (solid) { | ||
115 | solidDependencies.put(leftSet, rightSet); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // TODO perform proper minimization, | ||
120 | // see e.g. page 45 in http://www.cs.ubc.ca/~hkhosrav/db/slides/03.design%20theory.pdf | ||
121 | |||
122 | return Collections.unmodifiableMap(solidDependencies); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Adds a given dependency to a mutable accumulator. | ||
127 | * @since 1.5 | ||
128 | */ | ||
129 | public static <A> void includeDependency(Map<Set<A>, Set<A>> accumulator, Set<A> left, Set<A> right) { | ||
130 | Set<A> accumulatorRights = accumulator.computeIfAbsent(left, l -> new HashSet<>()); | ||
131 | accumulatorRights.addAll(right); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Adds all given dependencies to a mutable accumulator. | ||
136 | * @since 1.5 | ||
137 | */ | ||
138 | public static <A> void includeDependencies(Map<Set<A>, Set<A>> accumulator, Map<Set<A>, Set<A>> additionalDependencies) { | ||
139 | for (Entry<Set<A>, Set<A>> entry : additionalDependencies.entrySet()) { | ||
140 | includeDependency(accumulator, entry.getKey(), entry.getValue()); | ||
141 | } | ||
142 | } | ||
143 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java new file mode 100644 index 00000000..b4f848a7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java | |||
@@ -0,0 +1,62 @@ | |||
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.planning.helpers; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.function.BiFunction; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
16 | |||
17 | /** | ||
18 | * Helpers dealing with optionally present statistics information | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.1 | ||
22 | * | ||
23 | */ | ||
24 | public class StatisticsHelper { | ||
25 | |||
26 | private StatisticsHelper() { | ||
27 | // Hidden utility class constructor | ||
28 | } | ||
29 | |||
30 | public static Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy, | ||
31 | BiFunction<TupleMask, Accuracy, Optional<Long>> estimateCardinality) | ||
32 | { | ||
33 | if (groupMask.isIdentity()) return Optional.of(1.0); | ||
34 | |||
35 | Accuracy numeratorAccuracy = requiredAccuracy; | ||
36 | Accuracy denominatorAccuracy = requiredAccuracy.reciprocal(); | ||
37 | TupleMask identityMask = TupleMask.identity(groupMask.sourceWidth); | ||
38 | |||
39 | Optional<Long> totalCountEstimate = estimateCardinality.apply(identityMask, numeratorAccuracy); | ||
40 | Optional<Long> bucketCountEstimate = estimateCardinality.apply(groupMask, denominatorAccuracy); | ||
41 | |||
42 | return totalCountEstimate.flatMap(matchCount -> | ||
43 | bucketCountEstimate.map(bucketCount -> | ||
44 | bucketCount == 0L ? 0L : ((double) matchCount) / bucketCount | ||
45 | )); | ||
46 | } | ||
47 | |||
48 | public static Optional<Double> min(Optional<Double> a, Optional<Double> b) { | ||
49 | if (b.isPresent()) { | ||
50 | if (a.isPresent()) { | ||
51 | return Optional.of(Math.min(a.get(), b.get())); | ||
52 | } else return b; | ||
53 | } else return a; | ||
54 | } | ||
55 | public static Optional<Double> min(Optional<Double> a, double b) { | ||
56 | if (a.isPresent()) { | ||
57 | return Optional.of(Math.min(a.get(), b)); | ||
58 | } else return Optional.of(b); | ||
59 | } | ||
60 | |||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java new file mode 100644 index 00000000..926a591f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java | |||
@@ -0,0 +1,217 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.planning.helpers; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedList; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Queue; | ||
19 | import java.util.Set; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
23 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * @author Tamas Szabo | ||
32 | */ | ||
33 | public class TypeHelper { | ||
34 | |||
35 | private TypeHelper() { | ||
36 | // Hiding constructor for utility class | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Collects the type constraints for the specified collection of variables. The type constraints consist of the | ||
41 | * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with | ||
42 | * through equalities. | ||
43 | * | ||
44 | * @param variables | ||
45 | * the variables in question | ||
46 | * @param constraints | ||
47 | * the constraints in the pattern body | ||
48 | * @param context | ||
49 | * the query meta context | ||
50 | * @return the mapping from variable to set of type constraints | ||
51 | * @since 1.6 | ||
52 | */ | ||
53 | public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables, | ||
54 | Set<PConstraint> constraints, IQueryMetaContext context) { | ||
55 | Map<PVariable, Set<TypeJudgement>> typeMap = TypeHelper.inferUnaryTypes(constraints, context); | ||
56 | return inferUnaryTypesFor(variables, typeMap); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Collects the type constraints for the specified collection of variables. The type constraints consist of the | ||
61 | * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with | ||
62 | * through equalities. | ||
63 | * | ||
64 | * The method accepts a type map which is the result of the basic type inference from the | ||
65 | * {@link TypeHelper.inferUnaryTypes} method. The purpose of this method is that the type map can be reused across | ||
66 | * several calls to this method. | ||
67 | * | ||
68 | * @param variables | ||
69 | * the variables in question | ||
70 | * @param typeMap | ||
71 | * the type map of inference results | ||
72 | * @return the mapping from variable to set of type constraints | ||
73 | * @since 1.6 | ||
74 | */ | ||
75 | public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables, | ||
76 | Map<PVariable, Set<TypeJudgement>> typeMap) { | ||
77 | Map<PVariable, Set<IInputKey>> result = new HashMap<PVariable, Set<IInputKey>>(); | ||
78 | |||
79 | for (PVariable original : variables) { | ||
80 | // it can happen that the variable was unified into an other one due to equalities | ||
81 | Set<IInputKey> keys = new HashSet<IInputKey>(); | ||
82 | PVariable current = original; | ||
83 | |||
84 | while (current != null) { | ||
85 | Set<TypeJudgement> judgements = typeMap.get(current); | ||
86 | if (judgements != null) { | ||
87 | for (TypeJudgement judgement : judgements) { | ||
88 | keys.add(judgement.getInputKey()); | ||
89 | } | ||
90 | } | ||
91 | current = current.getDirectUnifiedInto(); | ||
92 | } | ||
93 | |||
94 | result.put(original, keys); | ||
95 | } | ||
96 | |||
97 | return result; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Infers unary type information for variables, based on the given constraints. | ||
102 | * | ||
103 | * Subsumptions are not taken into account. | ||
104 | * | ||
105 | * @param constraints | ||
106 | * the set of constraints to extract type info from | ||
107 | */ | ||
108 | public static Map<PVariable, Set<TypeJudgement>> inferUnaryTypes(Set<PConstraint> constraints, | ||
109 | IQueryMetaContext context) { | ||
110 | Set<TypeJudgement> equivalentJudgements = getDirectJudgements(constraints, context); | ||
111 | Set<TypeJudgement> impliedJudgements = typeClosure(equivalentJudgements, context); | ||
112 | |||
113 | Map<PVariable, Set<TypeJudgement>> results = new HashMap<PVariable, Set<TypeJudgement>>(); | ||
114 | for (TypeJudgement typeJudgement : impliedJudgements) { | ||
115 | final IInputKey inputKey = typeJudgement.getInputKey(); | ||
116 | if (inputKey.getArity() == 1) { | ||
117 | PVariable variable = (PVariable) typeJudgement.getVariablesTuple().get(0); | ||
118 | Set<TypeJudgement> inferredTypes = results.computeIfAbsent(variable, v -> new HashSet<>()); | ||
119 | inferredTypes.add(typeJudgement); | ||
120 | } | ||
121 | } | ||
122 | return results; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Gets direct judgements reported by constraints. No closure is applied yet. | ||
127 | */ | ||
128 | public static Set<TypeJudgement> getDirectJudgements(Set<PConstraint> constraints, IQueryMetaContext context) { | ||
129 | Set<TypeJudgement> equivalentJudgements = new HashSet<TypeJudgement>(); | ||
130 | for (PConstraint pConstraint : constraints) { | ||
131 | if (pConstraint instanceof ITypeInfoProviderConstraint) { | ||
132 | equivalentJudgements.addAll(((ITypeInfoProviderConstraint) pConstraint).getImpliedJudgements(context)); | ||
133 | } | ||
134 | } | ||
135 | return equivalentJudgements; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Calculates the closure of a set of type judgements, with respect to supertyping. | ||
140 | * | ||
141 | * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes | ||
142 | */ | ||
143 | public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> typesToClose, IQueryMetaContext context) { | ||
144 | return typeClosure(Collections.<TypeJudgement> emptySet(), typesToClose, context); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Calculates the closure of a set of type judgements (with respect to supertyping), where the closure has been | ||
149 | * calculated before for a given base set, but not for a separate delta set. | ||
150 | * <p> | ||
151 | * Precondition: the set (typesToClose MINUS delta) is already closed w.r.t. supertyping. | ||
152 | * | ||
153 | * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes | ||
154 | * @since 1.6 | ||
155 | */ | ||
156 | public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> preclosedBaseSet, Set<TypeJudgement> delta, | ||
157 | IQueryMetaContext context) { | ||
158 | Queue<TypeJudgement> queue = delta.stream().filter(input -> !preclosedBaseSet.contains(input)).collect(Collectors.toCollection(LinkedList::new)); | ||
159 | if (queue.isEmpty()) | ||
160 | return preclosedBaseSet; | ||
161 | |||
162 | Set<TypeJudgement> closure = new HashSet<TypeJudgement>(preclosedBaseSet); | ||
163 | |||
164 | Map<TypeJudgement, Set<TypeJudgement>> conditionalImplications = new HashMap<>(); | ||
165 | for (TypeJudgement typeJudgement : closure) { | ||
166 | conditionalImplications.putAll(typeJudgement.getConditionalImpliedJudgements(context)); | ||
167 | } | ||
168 | |||
169 | do { | ||
170 | TypeJudgement deltaType = queue.poll(); | ||
171 | if (closure.add(deltaType)) { | ||
172 | // direct implications | ||
173 | queue.addAll(deltaType.getDirectlyImpliedJudgements(context)); | ||
174 | |||
175 | // conditional implications, source key processed before, this is the condition key | ||
176 | final Set<TypeJudgement> implicationSet = conditionalImplications.get(deltaType); | ||
177 | if (implicationSet != null) { | ||
178 | queue.addAll(implicationSet); | ||
179 | } | ||
180 | |||
181 | // conditional implications, this is the source key | ||
182 | Map<TypeJudgement, Set<TypeJudgement>> deltaConditionalImplications = deltaType | ||
183 | .getConditionalImpliedJudgements(context); | ||
184 | for (Entry<TypeJudgement, Set<TypeJudgement>> entry : deltaConditionalImplications.entrySet()) { | ||
185 | if (closure.contains(entry.getKey())) { | ||
186 | // condition processed before | ||
187 | queue.addAll(entry.getValue()); | ||
188 | } else { | ||
189 | // condition not processed yet | ||
190 | conditionalImplications.computeIfAbsent(entry.getKey(), key -> new HashSet<>()) | ||
191 | .addAll(entry.getValue()); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } while (!queue.isEmpty()); | ||
196 | |||
197 | return closure; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Calculates a remainder set of types from a larger set, that are not subsumed by a given set of subsuming types. | ||
202 | * | ||
203 | * @param subsumableTypes | ||
204 | * a set of types from which some may be implied by the subsuming types | ||
205 | * @param subsumingTypes | ||
206 | * a set of types that may imply some of the subsuming types | ||
207 | * @return the collection of types in subsumableTypes that are NOT identical to or supertypes of any type in | ||
208 | * subsumingTypes. | ||
209 | */ | ||
210 | public static Set<TypeJudgement> subsumeTypes(Set<TypeJudgement> subsumableTypes, Set<TypeJudgement> subsumingTypes, | ||
211 | IQueryMetaContext context) { | ||
212 | Set<TypeJudgement> closure = typeClosure(subsumingTypes, context); | ||
213 | Set<TypeJudgement> subsumed = new HashSet<TypeJudgement>(subsumableTypes); | ||
214 | subsumed.removeAll(closure); | ||
215 | return subsumed; | ||
216 | } | ||
217 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java new file mode 100644 index 00000000..2c285b54 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
17 | |||
18 | /** | ||
19 | * Represents a constraint application on a single parent SubPlan. | ||
20 | * <p> Either a "selection" filter operation according to a deferred PConstraint (or transform in case of eval/aggregate), or | ||
21 | * alternatively a shorthand for PJoin + a PEnumerate on the right input for an enumerable PConstraint. | ||
22 | * | ||
23 | * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint, | ||
24 | * it is the responsibility of the compiler to check them for equality. | ||
25 | * | ||
26 | * @author Bergmann Gabor | ||
27 | * | ||
28 | */ | ||
29 | public class PApply extends POperation { | ||
30 | |||
31 | private PConstraint pConstraint; | ||
32 | |||
33 | public PApply(PConstraint pConstraint) { | ||
34 | super(); | ||
35 | this.pConstraint = pConstraint; | ||
36 | } | ||
37 | public PConstraint getPConstraint() { | ||
38 | return pConstraint; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public String getShortName() { | ||
43 | return String.format("APPLY_%s", pConstraint.toString()); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
48 | return Collections.singleton(pConstraint); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public int numParentSubPlans() { | ||
53 | return 1; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public void checkConsistency(SubPlan subPlan) { | ||
58 | super.checkConsistency(subPlan); | ||
59 | for (SubPlan parentPlan : subPlan.getParentPlans()) | ||
60 | Preconditions.checkArgument(!parentPlan.getAllEnforcedConstraints().contains(pConstraint), | ||
61 | "Double-checking constraint %s", pConstraint); | ||
62 | // TODO obtain context? | ||
63 | //if (pConstraint instanceof DeferredPConstraint) | ||
64 | // Preconditions.checkArgument(((DeferredPConstraint) pConstraint).isReadyAt(subPlan, context)) | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public int hashCode() { | ||
69 | final int prime = 31; | ||
70 | int result = 1; | ||
71 | result = prime | ||
72 | * result | ||
73 | + ((pConstraint == null) ? 0 : pConstraint | ||
74 | .hashCode()); | ||
75 | return result; | ||
76 | } | ||
77 | @Override | ||
78 | public boolean equals(Object obj) { | ||
79 | if (this == obj) | ||
80 | return true; | ||
81 | if (obj == null) | ||
82 | return false; | ||
83 | if (!(obj instanceof PApply)) | ||
84 | return false; | ||
85 | PApply other = (PApply) obj; | ||
86 | if (pConstraint == null) { | ||
87 | if (other.pConstraint != null) | ||
88 | return false; | ||
89 | } else if (!pConstraint.equals(other.pConstraint)) | ||
90 | return false; | ||
91 | return true; | ||
92 | } | ||
93 | |||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java new file mode 100644 index 00000000..a975d50c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | |||
17 | /** | ||
18 | * Represents a base relation defined by the instance set of an enumerable PConstraint; there are no parent SubPlans. | ||
19 | * | ||
20 | * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint, | ||
21 | * it is the responsibility of the compiler to check them for equality. | ||
22 | * @author Bergmann Gabor | ||
23 | * | ||
24 | */ | ||
25 | public class PEnumerate extends POperation { | ||
26 | |||
27 | EnumerablePConstraint enumerablePConstraint; | ||
28 | |||
29 | public PEnumerate(EnumerablePConstraint enumerablePConstraint) { | ||
30 | super(); | ||
31 | this.enumerablePConstraint = enumerablePConstraint; | ||
32 | } | ||
33 | public EnumerablePConstraint getEnumerablePConstraint() { | ||
34 | return enumerablePConstraint; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
39 | return Collections.singleton(enumerablePConstraint); | ||
40 | } | ||
41 | @Override | ||
42 | public int numParentSubPlans() { | ||
43 | return 0; | ||
44 | } | ||
45 | @Override | ||
46 | public String getShortName() { | ||
47 | return enumerablePConstraint.toString(); | ||
48 | } | ||
49 | @Override | ||
50 | public int hashCode() { | ||
51 | final int prime = 31; | ||
52 | int result = 1; | ||
53 | result = prime | ||
54 | * result | ||
55 | + ((enumerablePConstraint == null) ? 0 : enumerablePConstraint | ||
56 | .hashCode()); | ||
57 | return result; | ||
58 | } | ||
59 | @Override | ||
60 | public boolean equals(Object obj) { | ||
61 | if (this == obj) | ||
62 | return true; | ||
63 | if (obj == null) | ||
64 | return false; | ||
65 | if (!(obj instanceof PEnumerate)) | ||
66 | return false; | ||
67 | PEnumerate other = (PEnumerate) obj; | ||
68 | if (enumerablePConstraint == null) { | ||
69 | if (other.enumerablePConstraint != null) | ||
70 | return false; | ||
71 | } else if (!enumerablePConstraint.equals(other.enumerablePConstraint)) | ||
72 | return false; | ||
73 | return true; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java new file mode 100644 index 00000000..10e0a85a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | |||
16 | /** | ||
17 | * Represents a natural join of two parent SubPlans. | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public class PJoin extends POperation { | ||
22 | |||
23 | // // TODO leave here? is this a problem in equivalnece checking? | ||
24 | // private Set<PVariable> onVariables; | ||
25 | |||
26 | public PJoin(/*Set<PVariable> onVariables*/) { | ||
27 | super(); | ||
28 | //this.onVariables = new HashSet<PVariable>(onVariables); | ||
29 | } | ||
30 | // public Set<PVariable> getOnVariables() { | ||
31 | // return onVariables; | ||
32 | // } | ||
33 | |||
34 | @Override | ||
35 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
36 | return Collections.emptySet(); | ||
37 | } | ||
38 | @Override | ||
39 | public int numParentSubPlans() { | ||
40 | return 2; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public String getShortName() { | ||
45 | return "JOIN"; //String.format("JOIN_{%s}", Joiner.on(",").join(onVariables)); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public int hashCode() { | ||
50 | return getClass().hashCode(); | ||
51 | } | ||
52 | @Override | ||
53 | public boolean equals(Object obj) { | ||
54 | if (this == obj) | ||
55 | return true; | ||
56 | if (obj == null) | ||
57 | return false; | ||
58 | if (!(obj instanceof PJoin)) | ||
59 | return false; | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | |||
64 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java new file mode 100644 index 00000000..e71cf217 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
16 | |||
17 | /** | ||
18 | * Abstract superclass for representing a high-level query evaluation operation. | ||
19 | * | ||
20 | * <p> Subclasses correspond to various POperations modeled after relational algebra. | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * | ||
24 | */ | ||
25 | public abstract class POperation { | ||
26 | |||
27 | /** | ||
28 | * Newly enforced constraints | ||
29 | */ | ||
30 | public abstract Set<? extends PConstraint> getDeltaConstraints(); | ||
31 | |||
32 | public abstract String getShortName(); | ||
33 | |||
34 | /** | ||
35 | * @return the number of SubPlans that must be specified as parents | ||
36 | */ | ||
37 | public abstract int numParentSubPlans(); | ||
38 | |||
39 | /** | ||
40 | * Checks whether this constraint can be properly applied at the given SubPlan. | ||
41 | */ | ||
42 | public void checkConsistency(SubPlan subPlan) { | ||
43 | Preconditions.checkArgument(this == subPlan.getOperation(), "POperation misalignment"); | ||
44 | Preconditions.checkArgument(subPlan.getParentPlans().size() == numParentSubPlans(), "Incorrect number of parent SubPlans"); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String toString() { | ||
49 | return getShortName(); | ||
50 | } | ||
51 | |||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java new file mode 100644 index 00000000..d0539b2c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java | |||
@@ -0,0 +1,109 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
21 | |||
22 | /** | ||
23 | * Represents a projection of a single parent SubPlan onto a limited set of variables. | ||
24 | * <p> May optionally prescribe an ordering of variables (List, as opposed to Set). | ||
25 | * | ||
26 | * @author Bergmann Gabor | ||
27 | * | ||
28 | */ | ||
29 | public class PProject extends POperation { | ||
30 | |||
31 | private Collection<PVariable> toVariables; | ||
32 | private boolean ordered; | ||
33 | |||
34 | |||
35 | public PProject(Set<PVariable> toVariables) { | ||
36 | super(); | ||
37 | this.toVariables = toVariables; | ||
38 | this.ordered = false; | ||
39 | } | ||
40 | public PProject(List<PVariable> toVariables) { | ||
41 | super(); | ||
42 | this.toVariables = toVariables; | ||
43 | this.ordered = true; | ||
44 | } | ||
45 | |||
46 | public Collection<PVariable> getToVariables() { | ||
47 | return toVariables; | ||
48 | } | ||
49 | public boolean isOrdered() { | ||
50 | return ordered; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
55 | return Collections.emptySet(); | ||
56 | } | ||
57 | @Override | ||
58 | public int numParentSubPlans() { | ||
59 | return 1; | ||
60 | } | ||
61 | @Override | ||
62 | public void checkConsistency(SubPlan subPlan) { | ||
63 | super.checkConsistency(subPlan); | ||
64 | final SubPlan parentPlan = subPlan.getParentPlans().get(0); | ||
65 | |||
66 | Preconditions.checkArgument(parentPlan.getVisibleVariables().containsAll(toVariables), | ||
67 | () -> toVariables.stream() | ||
68 | .filter(input -> !parentPlan.getVisibleVariables().contains(input)).map(PVariable::getName) | ||
69 | .collect(Collectors.joining(",", "Variables missing from project: ", ""))); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public String getShortName() { | ||
74 | return String.format("PROJECT%s_{%s}", ordered? "!" : "", | ||
75 | toVariables.stream().map(PVariable::getName).collect(Collectors.joining(","))); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public int hashCode() { | ||
80 | final int prime = 31; | ||
81 | int result = 1; | ||
82 | result = prime * result + (ordered ? 1231 : 1237); | ||
83 | result = prime * result | ||
84 | + ((toVariables == null) ? 0 : toVariables.hashCode()); | ||
85 | return result; | ||
86 | } | ||
87 | @Override | ||
88 | public boolean equals(Object obj) { | ||
89 | if (this == obj) | ||
90 | return true; | ||
91 | if (obj == null) | ||
92 | return false; | ||
93 | if (!(obj instanceof PProject)) | ||
94 | return false; | ||
95 | PProject other = (PProject) obj; | ||
96 | if (ordered != other.ordered) | ||
97 | return false; | ||
98 | if (toVariables == null) { | ||
99 | if (other.toVariables != null) | ||
100 | return false; | ||
101 | } else if (!toVariables.equals(other.toVariables)) | ||
102 | return false; | ||
103 | return true; | ||
104 | } | ||
105 | |||
106 | |||
107 | |||
108 | |||
109 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java new file mode 100644 index 00000000..9e6ea10e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java | |||
@@ -0,0 +1,90 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.planning.operations; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | |||
20 | /** | ||
21 | * No constraints, and no parent SubPlan, just a (possibly empty) set of a priori known (input) variables. Satisfied by a single tuple. | ||
22 | * | ||
23 | * <p> Can also be used without a priori variables, | ||
24 | * e.g. as a "virtual parent" in extreme cases, | ||
25 | * such as <code>pattern foo(Bar) = {Bar = eval (3*4)} </code> | ||
26 | * | ||
27 | * @author Bergmann Gabor | ||
28 | * | ||
29 | */ | ||
30 | public class PStart extends POperation { | ||
31 | |||
32 | private Set<PVariable> aPrioriVariables; | ||
33 | |||
34 | |||
35 | public PStart(Set<PVariable> aPrioriVariables) { | ||
36 | super(); | ||
37 | this.aPrioriVariables = aPrioriVariables; | ||
38 | } | ||
39 | public PStart(PVariable... aPrioriVariables) { | ||
40 | this(new HashSet<PVariable>(Arrays.asList(aPrioriVariables))); | ||
41 | } | ||
42 | public Set<PVariable> getAPrioriVariables() { | ||
43 | return aPrioriVariables; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public String getShortName() { | ||
48 | return aPrioriVariables.stream().map(PVariable::getName).collect(Collectors.joining(",", "START_{", "}")); | ||
49 | } | ||
50 | @Override | ||
51 | public int numParentSubPlans() { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
57 | return Collections.emptySet(); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public int hashCode() { | ||
62 | final int prime = 31; | ||
63 | int result = 1; | ||
64 | result = prime | ||
65 | * result | ||
66 | + ((aPrioriVariables == null) ? 0 : aPrioriVariables.hashCode()); | ||
67 | return result; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean equals(Object obj) { | ||
72 | if (this == obj) | ||
73 | return true; | ||
74 | if (obj == null) | ||
75 | return false; | ||
76 | if (!(obj instanceof PStart)) | ||
77 | return false; | ||
78 | PStart other = (PStart) obj; | ||
79 | if (aPrioriVariables == null) { | ||
80 | if (other.aPrioriVariables != null) | ||
81 | return false; | ||
82 | } else if (!aPrioriVariables.equals(other.aPrioriVariables)) | ||
83 | return false; | ||
84 | return true; | ||
85 | } | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java new file mode 100644 index 00000000..eda4aa25 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
13 | |||
14 | import java.util.Collections; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | import java.util.concurrent.atomic.AtomicInteger; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public abstract class BasePConstraint implements PConstraint { | ||
25 | |||
26 | |||
27 | protected PBody pBody; | ||
28 | private final Set<PVariable> affectedVariables; | ||
29 | |||
30 | |||
31 | private final int sequentialID = nextID.getAndIncrement(); | ||
32 | |||
33 | // Use a static atomic integer to avoid race conditions when creating new constraints. | ||
34 | private static AtomicInteger nextID = new AtomicInteger(0); | ||
35 | |||
36 | public BasePConstraint(PBody pBody, Set<PVariable> affectedVariables) { | ||
37 | super(); | ||
38 | this.pBody = pBody; | ||
39 | this.affectedVariables = new HashSet<PVariable>(affectedVariables); | ||
40 | |||
41 | for (PVariable pVariable : affectedVariables) { | ||
42 | pVariable.refer(this); | ||
43 | } | ||
44 | pBody.registerConstraint(this); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String toString() { | ||
49 | return "PC[" + getClass().getSimpleName() + ":" + toStringRest() + "]"; | ||
50 | } | ||
51 | |||
52 | protected abstract String toStringRest(); | ||
53 | |||
54 | @Override | ||
55 | public Set<PVariable> getAffectedVariables() { | ||
56 | return affectedVariables; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
61 | return Collections.emptyMap(); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public void replaceVariable(PVariable obsolete, PVariable replacement) { | ||
66 | pBody.checkMutability(); | ||
67 | if (affectedVariables.remove(obsolete)) { | ||
68 | affectedVariables.add(replacement); | ||
69 | obsolete.unrefer(this); | ||
70 | replacement.refer(this); | ||
71 | doReplaceVariable(obsolete, replacement); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | protected abstract void doReplaceVariable(PVariable obsolete, PVariable replacement); | ||
76 | |||
77 | @Override | ||
78 | public void delete() { | ||
79 | pBody.checkMutability(); | ||
80 | for (PVariable pVariable : affectedVariables) { | ||
81 | pVariable.unrefer(this); | ||
82 | } | ||
83 | pBody.unregisterConstraint(this); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void checkSanity() { | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * For backwards compatibility. Equivalent to {@link #getBody()} | ||
92 | */ | ||
93 | public PBody getPSystem() { | ||
94 | return pBody; | ||
95 | } | ||
96 | /** | ||
97 | * @since 2.1 | ||
98 | */ | ||
99 | @Override | ||
100 | public PBody getBody() { | ||
101 | return pBody; | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public int getMonotonousID() { | ||
106 | return sequentialID; | ||
107 | } | ||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java new file mode 100644 index 00000000..d2bf088c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
16 | |||
17 | /** | ||
18 | * Any constraint that can only be checked on certain SubPlans (e.g. those plans that already contain some variables). | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public abstract class DeferredPConstraint extends BasePConstraint { | ||
24 | |||
25 | public DeferredPConstraint(PBody pBody, Set<PVariable> affectedVariables) { | ||
26 | super(pBody, affectedVariables); | ||
27 | } | ||
28 | |||
29 | public abstract boolean isReadyAt(SubPlan plan, IQueryMetaContext context); | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java new file mode 100644 index 00000000..9129aa47 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * A constraint for which all satisfying tuples of variable values can be enumerated at any point during run-time. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public abstract class EnumerablePConstraint extends BasePConstraint { | ||
23 | protected Tuple variablesTuple; | ||
24 | |||
25 | protected EnumerablePConstraint(PBody pBody, Tuple variablesTuple) { | ||
26 | super(pBody, variablesTuple.<PVariable> getDistinctElements()); | ||
27 | this.variablesTuple = variablesTuple; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
32 | variablesTuple = variablesTuple.replaceAll(obsolete, replacement); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | protected String toStringRest() { | ||
37 | String stringRestRest = toStringRestRest(); | ||
38 | String tupleString = "@" + variablesTuple.toString(); | ||
39 | return stringRestRest == null ? tupleString : ":" + stringRestRest + tupleString; | ||
40 | } | ||
41 | |||
42 | protected String toStringRestRest() { | ||
43 | return null; | ||
44 | } | ||
45 | |||
46 | public Tuple getVariablesTuple() { | ||
47 | return variablesTuple; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Set<PVariable> getDeducedVariables() { | ||
52 | return getAffectedVariables(); | ||
53 | } | ||
54 | |||
55 | public PVariable getVariableInTuple(int index) { | ||
56 | return (PVariable) this.variablesTuple.get(index); | ||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java new file mode 100644 index 00000000..686999f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java | |||
@@ -0,0 +1,42 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | /** | ||
12 | * An expression evaluator is used to execute arbitrary Java code during pattern matching. In order to include the | ||
13 | * evaluation in the planning seemlessly it is expected from the evaluator implementors to report all used PVariables by | ||
14 | * name. | ||
15 | * | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * | ||
18 | */ | ||
19 | public interface IExpressionEvaluator { | ||
20 | |||
21 | /** | ||
22 | * A textual description of the expression. Used only for debug purposes, but must not be null. | ||
23 | */ | ||
24 | String getShortDescription(); | ||
25 | |||
26 | /** | ||
27 | * All input parameter names should be reported correctly. | ||
28 | */ | ||
29 | Iterable<String> getInputParameterNames(); | ||
30 | |||
31 | /** | ||
32 | * The expression evaluator code | ||
33 | * | ||
34 | * @param provider | ||
35 | * the value provider is an engine-specific way of reading internal variable tuples to evaluate the | ||
36 | * expression with | ||
37 | * @return the result of the expression: in case of predicate evaluation the return value must be true or false; | ||
38 | * otherwise the result can be an arbitrary object. No null values should be returned. | ||
39 | * @throws Exception | ||
40 | */ | ||
41 | Object evaluateExpression(IValueProvider provider) throws Exception; | ||
42 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java new file mode 100644 index 00000000..8f647c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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.psystem; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | |||
15 | /** | ||
16 | * A {@link PConstraint} that implements this interface refers to a list of PQueries. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.8 | ||
20 | * | ||
21 | */ | ||
22 | public interface IMultiQueryReference { | ||
23 | |||
24 | Collection<PQuery> getReferredQueries(); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java new file mode 100644 index 00000000..9ee05b39 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * A {@link PConstraint} that implements this interface refers to a {@link PQuery}. | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * | ||
21 | */ | ||
22 | public interface IQueryReference extends IMultiQueryReference { | ||
23 | |||
24 | PQuery getReferredQuery(); | ||
25 | |||
26 | /** | ||
27 | * @since 2.8 | ||
28 | */ | ||
29 | @Override | ||
30 | default List<PQuery> getReferredQueries() { | ||
31 | return Collections.singletonList(getReferredQuery()); | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java new file mode 100644 index 00000000..e4c396d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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.psystem; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * Implementations of this interface take an arbitrary number of input relations with their contents and compute the | ||
18 | * tuples of a single output relation. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.8 | ||
22 | * | ||
23 | */ | ||
24 | public interface IRelationEvaluator { | ||
25 | |||
26 | /** | ||
27 | * A textual description of the evaluator. Used only for debug purposes, but must not be null. | ||
28 | */ | ||
29 | String getShortDescription(); | ||
30 | |||
31 | /** | ||
32 | * The relation evaluator code. For performance reasons, it is expected that the returned set is a mutable | ||
33 | * collection, and the caller must be allowed to actually perform mutations! | ||
34 | */ | ||
35 | Set<Tuple> evaluateRelation(List<Set<Tuple>> inputs) throws Exception; | ||
36 | |||
37 | /** | ||
38 | * For each input relation that this evaluator requires, this method returns the expected arities of the relations in order. | ||
39 | */ | ||
40 | List<Integer> getInputArities(); | ||
41 | |||
42 | /** | ||
43 | * Returns the arity of the output relation that this evaluator computes. | ||
44 | */ | ||
45 | int getOutputArity(); | ||
46 | |||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java new file mode 100644 index 00000000..b72035a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | import java.util.Set; | ||
21 | |||
22 | /** | ||
23 | * Common superinterface of enumerable and deferred type constraints. | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public interface ITypeConstraint extends ITypeInfoProviderConstraint { | ||
28 | |||
29 | public abstract TypeJudgement getEquivalentJudgement(); | ||
30 | |||
31 | /** | ||
32 | * Static internal utility class for implementations of {@link ITypeConstraint}s. | ||
33 | * @author Bergmann Gabor | ||
34 | */ | ||
35 | public static class TypeConstraintUtil { | ||
36 | |||
37 | private TypeConstraintUtil() { | ||
38 | // Hiding constructor for utility class | ||
39 | } | ||
40 | |||
41 | public static Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context, IInputKey inputKey, Tuple variablesTuple) { | ||
42 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
43 | |||
44 | Set<Entry<Set<Integer>, Set<Integer>>> dependencies = context.getFunctionalDependencies(inputKey).entrySet(); | ||
45 | for (Entry<Set<Integer>, Set<Integer>> dependency : dependencies) { | ||
46 | result.put( | ||
47 | transcribeVariables(dependency.getKey(), variablesTuple), | ||
48 | transcribeVariables(dependency.getValue(), variablesTuple) | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | return result; | ||
53 | } | ||
54 | |||
55 | private static Set<PVariable> transcribeVariables(Set<Integer> indices, Tuple variablesTuple) { | ||
56 | Set<PVariable> result = new HashSet<PVariable>(); | ||
57 | for (Integer index : indices) { | ||
58 | result.add((PVariable) variablesTuple.get(index)); | ||
59 | } | ||
60 | return result; | ||
61 | } | ||
62 | |||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java new file mode 100644 index 00000000..ff127d38 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java | |||
@@ -0,0 +1,28 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | |||
16 | /** | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public interface ITypeInfoProviderConstraint extends PConstraint { | ||
21 | |||
22 | /** | ||
23 | * Returns type information implied by this constraint. | ||
24 | * | ||
25 | */ | ||
26 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context); | ||
27 | |||
28 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java new file mode 100644 index 00000000..d959adc4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | /** | ||
12 | * Helper interface to get values from a tuple of variables. All pattern matching engines are expected to implement this | ||
13 | * to handle their internal structures. | ||
14 | * | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * | ||
17 | */ | ||
18 | public interface IValueProvider { | ||
19 | |||
20 | /** | ||
21 | * Returns the value of the selected variable | ||
22 | * @param variableName | ||
23 | * @return the value of the variable; never null | ||
24 | * @throws IllegalArgumentException if the variable is not defined | ||
25 | */ | ||
26 | Object getValue(String variableName); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java new file mode 100644 index 00000000..a82d12ec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java | |||
@@ -0,0 +1,56 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * Adds extra methods to the PQuery interface to initialize its contents. | ||
20 | * | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * | ||
23 | */ | ||
24 | public interface InitializablePQuery extends PQuery { | ||
25 | |||
26 | /** | ||
27 | * Sets the query status. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
28 | * | ||
29 | * @param status the new status | ||
30 | */ | ||
31 | void setStatus(PQueryStatus status); | ||
32 | |||
33 | /** | ||
34 | * Adds a detected error. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
35 | * | ||
36 | * @param problem the new problem | ||
37 | */ | ||
38 | void addError(PProblem problem); | ||
39 | |||
40 | /** | ||
41 | * Sets up the bodies of the pattern. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED | ||
42 | * uninitialized}. | ||
43 | * | ||
44 | * @param bodies | ||
45 | * @throws ViatraQueryRuntimeException | ||
46 | */ | ||
47 | void initializeBodies(Set<PBody> bodies); | ||
48 | |||
49 | /** | ||
50 | * Adds an annotation to the specification. Only applicable if the pattern is still | ||
51 | * {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
52 | * | ||
53 | * @param annotation | ||
54 | */ | ||
55 | void addAnnotation(PAnnotation annotation); | ||
56 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java new file mode 100644 index 00000000..91eea817 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * | ||
17 | */ | ||
18 | public abstract class KeyedEnumerablePConstraint<KeyType> extends EnumerablePConstraint { | ||
19 | |||
20 | protected KeyType supplierKey; | ||
21 | |||
22 | public KeyedEnumerablePConstraint(PBody pBody, Tuple variablesTuple, | ||
23 | KeyType supplierKey) { | ||
24 | super(pBody, variablesTuple); | ||
25 | this.supplierKey = supplierKey; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | protected String toStringRestRest() { | ||
30 | return supplierKey == null ? "$any(null)" : keyToString(); | ||
31 | } | ||
32 | |||
33 | protected abstract String keyToString(); | ||
34 | |||
35 | public KeyType getSupplierKey() { | ||
36 | return supplierKey; | ||
37 | } | ||
38 | |||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java new file mode 100644 index 00000000..c38dc23a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java | |||
@@ -0,0 +1,289 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedHashSet; | ||
16 | import java.util.List; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | import java.util.WeakHashMap; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * A set of constraints representing a pattern body | ||
33 | * | ||
34 | * @author Gabor Bergmann | ||
35 | * | ||
36 | */ | ||
37 | public class PBody implements PTraceable { | ||
38 | |||
39 | public static final String VIRTUAL_VARIABLE_PREFIX = ".virtual"; | ||
40 | private static final String VIRTUAL_VARIABLE_PATTERN = VIRTUAL_VARIABLE_PREFIX + "{%d}"; | ||
41 | |||
42 | private PQuery query; | ||
43 | |||
44 | /** | ||
45 | * If null, then parent query status is reused | ||
46 | */ | ||
47 | private PQueryStatus status = PQueryStatus.UNINITIALIZED; | ||
48 | |||
49 | private Set<PVariable> allVariables; | ||
50 | private Set<PVariable> uniqueVariables; | ||
51 | private List<ExportedParameter> symbolicParameters; | ||
52 | private Map<Object, PVariable> variablesByName; | ||
53 | private Set<PConstraint> constraints; | ||
54 | private int nextVirtualNodeID; | ||
55 | private PDisjunction containerDisjunction; | ||
56 | |||
57 | public PBody(PQuery query) { | ||
58 | super(); | ||
59 | this.query = query; | ||
60 | allVariables = new LinkedHashSet<>(); | ||
61 | uniqueVariables = new LinkedHashSet<>(); | ||
62 | variablesByName = new HashMap<>(); | ||
63 | constraints = new LinkedHashSet<>(); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @return whether the submission of the new variable was successful | ||
68 | */ | ||
69 | private boolean addVariable(PVariable var) { | ||
70 | checkMutability(); | ||
71 | Object name = var.getName(); | ||
72 | if (!variablesByName.containsKey(name)) { | ||
73 | allVariables.add(var); | ||
74 | if (var.isUnique()) | ||
75 | uniqueVariables.add(var); | ||
76 | variablesByName.put(name, var); | ||
77 | return true; | ||
78 | } else { | ||
79 | return false; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Use this method to add a newly created constraint to the pSystem. | ||
85 | * | ||
86 | * @return whether the submission of the new constraint was successful | ||
87 | */ | ||
88 | boolean registerConstraint(PConstraint constraint) { | ||
89 | checkMutability(); | ||
90 | return constraints.add(constraint); | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Use this method to remove an obsolete constraint from the pSystem. | ||
95 | * | ||
96 | * @return whether the removal of the constraint was successful | ||
97 | */ | ||
98 | boolean unregisterConstraint(PConstraint constraint) { | ||
99 | checkMutability(); | ||
100 | return constraints.remove(constraint); | ||
101 | } | ||
102 | |||
103 | @SuppressWarnings("unchecked") | ||
104 | public <ConstraintType> Set<ConstraintType> getConstraintsOfType(Class<ConstraintType> constraintClass) { | ||
105 | Set<ConstraintType> result = new HashSet<ConstraintType>(); | ||
106 | for (PConstraint pConstraint : constraints) { | ||
107 | if (constraintClass.isInstance(pConstraint)) | ||
108 | result.add((ConstraintType) pConstraint); | ||
109 | } | ||
110 | return result; | ||
111 | } | ||
112 | |||
113 | public PVariable newVirtualVariable() { | ||
114 | checkMutability(); | ||
115 | String name; | ||
116 | do { | ||
117 | |||
118 | name = String.format(VIRTUAL_VARIABLE_PATTERN, nextVirtualNodeID++); | ||
119 | } while (variablesByName.containsKey(name)); | ||
120 | PVariable var = new PVariable(this, name, true); | ||
121 | addVariable(var); | ||
122 | return var; | ||
123 | } | ||
124 | |||
125 | public PVariable newVirtualVariable(String name) { | ||
126 | checkMutability(); | ||
127 | Preconditions.checkArgument(!variablesByName.containsKey(name), "ID %s already used for a virtual variable", name); | ||
128 | PVariable var = new PVariable(this, name, true); | ||
129 | addVariable(var); | ||
130 | return var; | ||
131 | } | ||
132 | |||
133 | public PVariable newConstantVariable(Object value) { | ||
134 | checkMutability(); | ||
135 | PVariable virtual = newVirtualVariable(); | ||
136 | new ConstantValue(this, virtual, value); | ||
137 | return virtual; | ||
138 | } | ||
139 | |||
140 | public Set<PVariable> getAllVariables() { | ||
141 | return allVariables; | ||
142 | } | ||
143 | |||
144 | public Set<PVariable> getUniqueVariables() { | ||
145 | return uniqueVariables; | ||
146 | } | ||
147 | |||
148 | private PVariable getVariableByName(Object name) { | ||
149 | return variablesByName.get(name).getUnifiedIntoRoot(); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Find a PVariable by name | ||
154 | * | ||
155 | * @param name | ||
156 | * @return the found variable | ||
157 | * @throws IllegalArgumentException | ||
158 | * if no PVariable is found with the selected name | ||
159 | */ | ||
160 | public PVariable getVariableByNameChecked(Object name) { | ||
161 | if (!variablesByName.containsKey(name)) | ||
162 | throw new IllegalArgumentException(String.format("Cannot find PVariable %s", name)); | ||
163 | return getVariableByName(name); | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Finds and returns a PVariable by name. If no PVariable exists with the name in the body, a new one is created. If | ||
168 | * the name of the variable starts with {@value #VIRTUAL_VARIABLE_PREFIX}, the created variable will be considered | ||
169 | * virtual. | ||
170 | * | ||
171 | * @param name | ||
172 | * @return a PVariable with the selected name; never null | ||
173 | */ | ||
174 | public PVariable getOrCreateVariableByName(String name) { | ||
175 | checkMutability(); | ||
176 | if (!variablesByName.containsKey(name)) { | ||
177 | addVariable(new PVariable(this, name, name.startsWith(VIRTUAL_VARIABLE_PREFIX))); | ||
178 | } | ||
179 | return getVariableByName(name); | ||
180 | } | ||
181 | |||
182 | public Set<PConstraint> getConstraints() { | ||
183 | return constraints; | ||
184 | } | ||
185 | |||
186 | public PQuery getPattern() { | ||
187 | return query; | ||
188 | } | ||
189 | |||
190 | void noLongerUnique(PVariable pVariable) { | ||
191 | assert (!pVariable.isUnique()); | ||
192 | uniqueVariables.remove(pVariable); | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Returns the symbolic parameters of the body. </p> | ||
197 | * | ||
198 | * <p> | ||
199 | * <strong>Warning</strong>: if two PVariables are unified, the returned list changes. If you want to have a stable | ||
200 | * version, consider using {@link #getSymbolicParameters()}. | ||
201 | * | ||
202 | * @return a non-null, but possibly empty list | ||
203 | */ | ||
204 | public List<PVariable> getSymbolicParameterVariables() { | ||
205 | return getSymbolicParameters().stream().map(ExportedParameter::getParameterVariable) | ||
206 | .collect(Collectors.toList()); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * Returns the exported parameter constraints of the body. | ||
211 | * | ||
212 | * @return a non-null, but possibly empty list | ||
213 | */ | ||
214 | public List<ExportedParameter> getSymbolicParameters() { | ||
215 | if (symbolicParameters == null) | ||
216 | symbolicParameters = new ArrayList<>(); | ||
217 | return symbolicParameters; | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * Sets the exported parameter constraints of the body, if this instance is mutable. | ||
222 | * @param symbolicParameters the new value | ||
223 | */ | ||
224 | public void setSymbolicParameters(List<ExportedParameter> symbolicParameters) { | ||
225 | checkMutability(); | ||
226 | this.symbolicParameters = new ArrayList<>(symbolicParameters); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Sets a specific status for the body. If set, the parent PQuery status will not be checked; if set to null, its corresponding PQuery | ||
231 | * status is checked for mutability. | ||
232 | * | ||
233 | * @param status | ||
234 | * the status to set | ||
235 | */ | ||
236 | public void setStatus(PQueryStatus status) { | ||
237 | this.status = status; | ||
238 | } | ||
239 | |||
240 | public boolean isMutable() { | ||
241 | if (status == null) { | ||
242 | return query.isMutable(); | ||
243 | } else { | ||
244 | return status.equals(PQueryStatus.UNINITIALIZED); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | void checkMutability() { | ||
249 | if (status == null) { | ||
250 | query.checkMutability(); | ||
251 | } else { | ||
252 | Preconditions.checkState(status.equals(PQueryStatus.UNINITIALIZED), "Initialized queries are not mutable"); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Returns the disjunction the body is contained with. This disjunction may either be the | ||
258 | * {@link PQuery#getDisjunctBodies() canonical disjunction of the corresponding query} or something equivalent. | ||
259 | * | ||
260 | * @return the container disjunction of the body. Can be null if body is not in a disjunction yet. | ||
261 | */ | ||
262 | public PDisjunction getContainerDisjunction() { | ||
263 | return containerDisjunction; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * @param containerDisjunction the containerDisjunction to set | ||
268 | */ | ||
269 | public void setContainerDisjunction(PDisjunction containerDisjunction) { | ||
270 | Preconditions.checkArgument(query.equals(containerDisjunction.getQuery()), "Disjunction of pattern %s incompatible with body %s", containerDisjunction.getQuery().getFullyQualifiedName(), query.getFullyQualifiedName()); | ||
271 | Preconditions.checkState(this.containerDisjunction == null, "Disjunction is already set."); | ||
272 | this.containerDisjunction = containerDisjunction; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * All unary input keys directly prescribed by constraints, grouped by variable. | ||
277 | * <p> to supertype inference or subsumption applied at this point. | ||
278 | */ | ||
279 | public Map<PVariable, Set<TypeJudgement>> getAllUnaryTypeRestrictions(IQueryMetaContext context) { | ||
280 | Map<PVariable, Set<TypeJudgement>> currentRestrictions = allUnaryTypeRestrictions.get(context); | ||
281 | if (currentRestrictions == null) { | ||
282 | currentRestrictions = TypeHelper.inferUnaryTypes(getConstraints(), context); | ||
283 | allUnaryTypeRestrictions.put(context, currentRestrictions); | ||
284 | } | ||
285 | return currentRestrictions; | ||
286 | } | ||
287 | private WeakHashMap<IQueryMetaContext, Map<PVariable, Set<TypeJudgement>>> allUnaryTypeRestrictions = new WeakHashMap<IQueryMetaContext, Map<PVariable,Set<TypeJudgement>>>(); | ||
288 | |||
289 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java new file mode 100644 index 00000000..ae2c4632 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
18 | |||
19 | /** | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public interface PConstraint extends PTraceable { | ||
24 | |||
25 | /** | ||
26 | * @since 2.1 | ||
27 | * @return the query body this constraint belongs to | ||
28 | */ | ||
29 | public PBody getBody(); | ||
30 | |||
31 | /** | ||
32 | * All variables affected by this constraint. | ||
33 | */ | ||
34 | public Set<PVariable> getAffectedVariables(); | ||
35 | |||
36 | /** | ||
37 | * The set of variables whose potential values can be enumerated (once all non-deduced variables have known values). | ||
38 | */ | ||
39 | public Set<PVariable> getDeducedVariables(); | ||
40 | |||
41 | /** | ||
42 | * A (preferably minimal) cover of known functional dependencies between variables. | ||
43 | * @noreference Use {@link QueryAnalyzer} instead to properly handle dependencies of pattern calls. | ||
44 | * @return non-trivial functional dependencies in the form of {variables} --> {variables}, where dependencies with the same lhs are unified. | ||
45 | */ | ||
46 | public Map<Set<PVariable>,Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context); | ||
47 | |||
48 | public void replaceVariable(PVariable obsolete, PVariable replacement); | ||
49 | |||
50 | public void delete(); | ||
51 | |||
52 | public void checkSanity(); | ||
53 | |||
54 | /** | ||
55 | * Returns an integer ID that is guaranteed to increase strictly monotonously for constraints within a pBody. | ||
56 | */ | ||
57 | public abstract int getMonotonousID(); | ||
58 | |||
59 | |||
60 | /** | ||
61 | * A comparator that orders constraints by their {@link #getMonotonousID() monotonous identifiers}. Should only used | ||
62 | * for tiebreaking in other comparators. | ||
63 | * | ||
64 | * @since 2.0 | ||
65 | */ | ||
66 | public static final Comparator<PConstraint> COMPARE_BY_MONOTONOUS_ID = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); | ||
67 | |||
68 | |||
69 | |||
70 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java new file mode 100644 index 00000000..f0241a9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Dénes Harmath, IncQueryLabs 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.psystem; | ||
10 | |||
11 | /** | ||
12 | * Marker interface for PSystem elements that can be traced. | ||
13 | * @since 1.6 | ||
14 | */ | ||
15 | public interface PTraceable { | ||
16 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java new file mode 100644 index 00000000..b6ea4861 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java | |||
@@ -0,0 +1,203 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * | ||
18 | */ | ||
19 | public class PVariable { | ||
20 | private PBody pBody; | ||
21 | /** | ||
22 | * The name of the pattern variable. This is the unique key of the pattern node. | ||
23 | */ | ||
24 | private String name; | ||
25 | /** | ||
26 | * virtual pVariables are nodes that do not correspond to actual pattern variables; they represent constants or Term | ||
27 | * substitutes | ||
28 | */ | ||
29 | private boolean virtual; | ||
30 | |||
31 | /** | ||
32 | * Set of constraints that mention this variable | ||
33 | */ | ||
34 | private Set<PConstraint> referringConstraints; | ||
35 | |||
36 | /** | ||
37 | * Determines whether there are any constraints that can deduce this variable | ||
38 | */ | ||
39 | private Boolean deducable; | ||
40 | |||
41 | /** | ||
42 | * Another PVariable this variable has been unified into. Please use the other variable instead of this. Null iff | ||
43 | * this is still a first-class variable. | ||
44 | */ | ||
45 | private PVariable unifiedInto; | ||
46 | |||
47 | PVariable(PBody pBody, String name) { | ||
48 | this(pBody, name, false); | ||
49 | } | ||
50 | |||
51 | PVariable(PBody pBody, String name, boolean virtual) { | ||
52 | super(); | ||
53 | this.pBody = pBody; | ||
54 | this.name = name; | ||
55 | this.virtual = virtual; | ||
56 | // this.exportedParameter = false; | ||
57 | this.referringConstraints = new HashSet<PConstraint>(); | ||
58 | this.unifiedInto = null; | ||
59 | this.deducable = false; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Replaces this variable with a given other, resulting in their unification. This variable will no longer be | ||
64 | * unique. | ||
65 | * | ||
66 | * @param replacement | ||
67 | */ | ||
68 | public void unifyInto(PVariable replacement) { | ||
69 | pBody.checkMutability(); | ||
70 | replacementCheck(); | ||
71 | replacement = replacement.getUnifiedIntoRoot(); | ||
72 | |||
73 | if (this.equals(replacement)) | ||
74 | return; | ||
75 | |||
76 | if (!this.isVirtual() && replacement.isVirtual()) { | ||
77 | replacement.unifyInto(this); | ||
78 | } else { | ||
79 | // replacement.referringConstraints.addAll(this.referringConstraints); | ||
80 | // replacement.exportedParameter |= this.exportedParameter; | ||
81 | replacement.virtual &= this.virtual; | ||
82 | if (replacement.deducable != null && this.deducable != null) | ||
83 | replacement.deducable |= this.deducable; | ||
84 | else | ||
85 | replacement.deducable = null; | ||
86 | Set<PConstraint> snapshotConstraints = // avoid ConcurrentModificationX | ||
87 | new HashSet<PConstraint>(this.referringConstraints); | ||
88 | for (PConstraint constraint : snapshotConstraints) { | ||
89 | constraint.replaceVariable(this, replacement); | ||
90 | } | ||
91 | // replacementCheck() will fail from this point | ||
92 | this.unifiedInto = replacement; | ||
93 | pBody.noLongerUnique(this); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Determines whether there are any constraints that can deduce this variable | ||
99 | */ | ||
100 | public boolean isDeducable() { | ||
101 | replacementCheck(); | ||
102 | if (deducable == null) { | ||
103 | deducable = false; | ||
104 | for (PConstraint pConstraint : getReferringConstraints()) { | ||
105 | if (pConstraint.getDeducedVariables().contains(this)) { | ||
106 | deducable = true; | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | return deducable; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Register that this variable is referred by the given constraint. | ||
116 | * | ||
117 | * @param constraint | ||
118 | */ | ||
119 | public void refer(PConstraint constraint) { | ||
120 | pBody.checkMutability(); | ||
121 | replacementCheck(); | ||
122 | deducable = null; | ||
123 | referringConstraints.add(constraint); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Register that this variable is no longer referred by the given constraint. | ||
128 | * | ||
129 | * @param constraint | ||
130 | */ | ||
131 | public void unrefer(PConstraint constraint) { | ||
132 | pBody.checkMutability(); | ||
133 | replacementCheck(); | ||
134 | deducable = null; | ||
135 | referringConstraints.remove(constraint); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * @return the name of the pattern variable. This is the unique key of the pattern node. | ||
140 | */ | ||
141 | public String getName() { | ||
142 | replacementCheck(); | ||
143 | return name; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * @return the virtual | ||
148 | */ | ||
149 | public boolean isVirtual() { | ||
150 | replacementCheck(); | ||
151 | return virtual; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * @return the referringConstraints | ||
156 | */ | ||
157 | public Set<PConstraint> getReferringConstraints() { | ||
158 | replacementCheck(); | ||
159 | return referringConstraints; | ||
160 | } | ||
161 | |||
162 | @SuppressWarnings("unchecked") | ||
163 | public <ConstraintType> Set<ConstraintType> getReferringConstraintsOfType(Class<ConstraintType> constraintClass) { | ||
164 | replacementCheck(); | ||
165 | Set<ConstraintType> result = new HashSet<ConstraintType>(); | ||
166 | for (PConstraint pConstraint : referringConstraints) { | ||
167 | if (constraintClass.isInstance(pConstraint)) | ||
168 | result.add((ConstraintType) pConstraint); | ||
169 | } | ||
170 | return result; | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | public String toString() { | ||
175 | // replacementCheck(); | ||
176 | return name;// + ":PatternNode"; | ||
177 | } | ||
178 | |||
179 | public PVariable getDirectUnifiedInto() { | ||
180 | return unifiedInto; | ||
181 | } | ||
182 | |||
183 | public PVariable getUnifiedIntoRoot() { | ||
184 | PVariable nextUnified = unifiedInto; | ||
185 | PVariable oldUnifiedInto = this; | ||
186 | while (nextUnified != null) { | ||
187 | oldUnifiedInto = nextUnified; | ||
188 | nextUnified = oldUnifiedInto.getDirectUnifiedInto(); | ||
189 | } | ||
190 | return oldUnifiedInto; // unifiedInto; | ||
191 | } | ||
192 | |||
193 | public boolean isUnique() { | ||
194 | return unifiedInto == null; | ||
195 | } | ||
196 | |||
197 | private void replacementCheck() { | ||
198 | if (unifiedInto != null) | ||
199 | throw new IllegalStateException("Illegal usage of variable " + name + " which has been replaced with " | ||
200 | + unifiedInto.name); | ||
201 | } | ||
202 | |||
203 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java new file mode 100644 index 00000000..4447b225 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java | |||
@@ -0,0 +1,153 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.stream.Collectors; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * A judgement that means that the given tuple of variables will represent a tuple of values that is a member of the extensional relation identified by the given input key. | ||
28 | * @author Bergmann Gabor | ||
29 | * | ||
30 | */ | ||
31 | public class TypeJudgement { | ||
32 | |||
33 | private IInputKey inputKey; | ||
34 | private Tuple variablesTuple; | ||
35 | /** | ||
36 | * @param inputKey | ||
37 | * @param variablesTuple | ||
38 | */ | ||
39 | public TypeJudgement(IInputKey inputKey, Tuple variablesTuple) { | ||
40 | super(); | ||
41 | this.inputKey = inputKey; | ||
42 | this.variablesTuple = variablesTuple; | ||
43 | } | ||
44 | public IInputKey getInputKey() { | ||
45 | return inputKey; | ||
46 | } | ||
47 | public Tuple getVariablesTuple() { | ||
48 | return variablesTuple; | ||
49 | } | ||
50 | @Override | ||
51 | public int hashCode() { | ||
52 | final int prime = 31; | ||
53 | int result = 1; | ||
54 | result = prime * result | ||
55 | + ((inputKey == null) ? 0 : inputKey.hashCode()); | ||
56 | result = prime * result | ||
57 | + ((variablesTuple == null) ? 0 : variablesTuple.hashCode()); | ||
58 | return result; | ||
59 | } | ||
60 | @Override | ||
61 | public boolean equals(Object obj) { | ||
62 | if (this == obj) | ||
63 | return true; | ||
64 | if (obj == null) | ||
65 | return false; | ||
66 | if (!(obj instanceof TypeJudgement)) | ||
67 | return false; | ||
68 | TypeJudgement other = (TypeJudgement) obj; | ||
69 | if (inputKey == null) { | ||
70 | if (other.inputKey != null) | ||
71 | return false; | ||
72 | } else if (!inputKey.equals(other.inputKey)) | ||
73 | return false; | ||
74 | if (variablesTuple == null) { | ||
75 | if (other.variablesTuple != null) | ||
76 | return false; | ||
77 | } else if (!variablesTuple.equals(other.variablesTuple)) | ||
78 | return false; | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | public Set<TypeJudgement> getDirectlyImpliedJudgements(IQueryMetaContext context) { | ||
83 | Set<TypeJudgement> results = new HashSet<TypeJudgement>(); | ||
84 | results.add(this); | ||
85 | |||
86 | Collection<InputKeyImplication> implications = context.getImplications(this.inputKey); | ||
87 | for (InputKeyImplication inputKeyImplication : implications) { | ||
88 | results.add( | ||
89 | transcribeImplication(inputKeyImplication) | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | return results; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * @since 1.6 | ||
98 | */ | ||
99 | public Set<TypeJudgement> getWeakenedAlternativeJudgements(IQueryMetaContext context) { | ||
100 | Set<TypeJudgement> results = new HashSet<TypeJudgement>(); | ||
101 | |||
102 | Collection<InputKeyImplication> implications = context.getWeakenedAlternatives(this.inputKey); | ||
103 | for (InputKeyImplication inputKeyImplication : implications) { | ||
104 | results.add( | ||
105 | transcribeImplication(inputKeyImplication) | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | return results; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | public Map<TypeJudgement, Set<TypeJudgement>> getConditionalImpliedJudgements(IQueryMetaContext context) { | ||
116 | return context.getConditionalImplications(this.inputKey).entrySet().stream().collect(Collectors.toMap( | ||
117 | entry -> transcribeImplication(entry.getKey()), | ||
118 | entry -> entry.getValue().stream().map(this::transcribeImplication).collect(Collectors.toSet()))); | ||
119 | } | ||
120 | |||
121 | |||
122 | |||
123 | private TypeJudgement transcribeImplication(InputKeyImplication inputKeyImplication) { | ||
124 | return new TypeJudgement( | ||
125 | inputKeyImplication.getImpliedKey(), | ||
126 | transcribeVariablesToTuple(inputKeyImplication.getImpliedIndices()) | ||
127 | ); | ||
128 | } | ||
129 | private Tuple transcribeVariablesToTuple(List<Integer> indices) { | ||
130 | Object[] elements = new Object[indices.size()]; | ||
131 | for (int i = 0; i < indices.size(); ++i) | ||
132 | elements[i] = variablesTuple.get(indices.get(i)); | ||
133 | return Tuples.flatTupleOf(elements); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public String toString() { | ||
138 | return "TypeJudgement:" + inputKey.getPrettyPrintableName() + "@" + variablesTuple.toString(); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Creates this judgement as a direct type constraint in the given PBody under construction. | ||
143 | * <p> pre: the variables tuple must be formed of variables of that PBody. | ||
144 | * @since 1.6 | ||
145 | */ | ||
146 | public PConstraint createConstraintFor(PBody pBody) { | ||
147 | if (inputKey.isEnumerable()) { | ||
148 | return new TypeConstraint(pBody, variablesTuple, inputKey); | ||
149 | } else { | ||
150 | return new TypeFilterConstraint(pBody, variablesTuple, inputKey); | ||
151 | } | ||
152 | } | ||
153 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java new file mode 100644 index 00000000..8ea6bb93 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
16 | |||
17 | /** | ||
18 | * A kind of deferred constraint that can only be checked when a set of deferring variables are all present in a plan. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public abstract class VariableDeferredPConstraint extends DeferredPConstraint { | ||
24 | |||
25 | public VariableDeferredPConstraint(PBody pBody, | ||
26 | Set<PVariable> affectedVariables) { | ||
27 | super(pBody, affectedVariables); | ||
28 | } | ||
29 | |||
30 | public abstract Set<PVariable> getDeferringVariables(); | ||
31 | |||
32 | /** | ||
33 | * Refine further if needed | ||
34 | */ | ||
35 | @Override | ||
36 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
37 | return plan.getVisibleVariables().containsAll(getDeferringVariables()); | ||
38 | } | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java new file mode 100644 index 00000000..63a37bbe --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.psystem.aggregations; | ||
10 | |||
11 | /** | ||
12 | * | ||
13 | * An aggregation operator that does not store interim results beyond the final aggregated value. | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.4 | ||
16 | */ | ||
17 | public abstract class AbstractMemorylessAggregationOperator<Domain, AggregateResult> | ||
18 | implements IMultisetAggregationOperator<Domain, AggregateResult, AggregateResult> | ||
19 | { | ||
20 | |||
21 | @Override | ||
22 | public AggregateResult getAggregate(AggregateResult result) { | ||
23 | return result; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public AggregateResult clone(AggregateResult original) { | ||
28 | return original; | ||
29 | } | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java new file mode 100644 index 00000000..4cc40a2b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, 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.psystem.aggregations; | ||
10 | |||
11 | import java.lang.annotation.ElementType; | ||
12 | import java.lang.annotation.Inherited; | ||
13 | import java.lang.annotation.Retention; | ||
14 | import java.lang.annotation.RetentionPolicy; | ||
15 | import java.lang.annotation.Target; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.aggregators.count; | ||
18 | |||
19 | /** | ||
20 | * The aggregator type annotation describes the type constraints for the selected aggregator. In version 1.4, two kinds of | ||
21 | * aggregators are supported: | ||
22 | * | ||
23 | * <ol> | ||
24 | * <li>An aggregator that does not consider any parameter value from the call ({@link count}), just calculates the | ||
25 | * number of matches. This is represented by a single {@link Void} and a single corresponding return type.</li> | ||
26 | * <li>An aggregator that considers a single parameter from the call, and executes some aggregate operations over it. | ||
27 | * Such an aggregate operation can be defined over multiple types, where each possible parameter type has a corresponding return type declared.</li> | ||
28 | * </ol> | ||
29 | * | ||
30 | * <strong>Important!</strong> The parameterTypes and returnTypes arrays must have | ||
31 | * <ul> | ||
32 | * <li>The same number of classes defined each.</li> | ||
33 | * <li>Items are corresponded by index.</li> | ||
34 | * <li>Items should represent data types</li> | ||
35 | * </ul> | ||
36 | * | ||
37 | * @author Zoltan Ujhelyi | ||
38 | * @since 1.4 | ||
39 | * | ||
40 | */ | ||
41 | @Target({ ElementType.TYPE }) | ||
42 | @Retention(RetentionPolicy.RUNTIME) | ||
43 | @Inherited | ||
44 | public @interface AggregatorType { | ||
45 | |||
46 | Class<?>[] parameterTypes(); | ||
47 | |||
48 | Class<?>[] returnTypes(); | ||
49 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java new file mode 100644 index 00000000..e6972544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.psystem.aggregations; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
13 | |||
14 | /** | ||
15 | * Augments an aggregator operator with type bindings for the type of values being aggregated and the aggregate result. | ||
16 | * <p> In case of <em>count</em>, the operator should be null. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class BoundAggregator { | ||
21 | private final IMultisetAggregationOperator<?, ?, ?> operator; | ||
22 | private final Class<?> domainType; | ||
23 | private final Class<?> aggregateResultType; | ||
24 | |||
25 | public BoundAggregator(IMultisetAggregationOperator<?, ?, ?> operator, | ||
26 | Class<?> domainType, | ||
27 | Class<?> aggregateResultType) { | ||
28 | super(); | ||
29 | this.operator = operator; | ||
30 | this.domainType = domainType; | ||
31 | this.aggregateResultType = aggregateResultType; | ||
32 | } | ||
33 | |||
34 | public IMultisetAggregationOperator<?, ?, ?> getOperator() { | ||
35 | return operator; | ||
36 | } | ||
37 | |||
38 | public Class<?> getDomainType() { | ||
39 | return domainType; | ||
40 | } | ||
41 | |||
42 | public Class<?> getAggregateResultType() { | ||
43 | return aggregateResultType; | ||
44 | } | ||
45 | |||
46 | public IInputKey getDomainTypeAsInputKey() { | ||
47 | return toJavaInputKey(domainType); | ||
48 | } | ||
49 | |||
50 | public IInputKey getAggregateResultTypeAsInputKey() { | ||
51 | return toJavaInputKey(aggregateResultType); | ||
52 | } | ||
53 | |||
54 | private static IInputKey toJavaInputKey(Class<?> type) { | ||
55 | if (type==null) { | ||
56 | return null; | ||
57 | } else { | ||
58 | return new JavaTransitiveInstancesKey(type); | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java new file mode 100644 index 00000000..c970bd6a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.psystem.aggregations; | ||
10 | |||
11 | /** | ||
12 | * | ||
13 | * Describes an aggregation operator keyword, potentially with type polymorphism. The actual runtime | ||
14 | * {@link IMultisetAggregationOperator} that implements the aggregation logic may depend on the type context. | ||
15 | * | ||
16 | * <p> | ||
17 | * Implementors are suggested to use lower-case classnames (as it will end up in the language) and are required use the | ||
18 | * annotation {@link AggregatorType} to indicate type inference rules. | ||
19 | * | ||
20 | * <p> | ||
21 | * <strong>Important!</strong> Implemented aggregators must be (1) deterministic (2) pure and (3)support incremental | ||
22 | * value updates in the internal operation. | ||
23 | * | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * @since 1.4 | ||
26 | */ | ||
27 | |||
28 | public interface IAggregatorFactory { | ||
29 | |||
30 | /** | ||
31 | * Given type parameters selected from {@link AggregatorType} annotations, returns a run-time aggregator operator | ||
32 | * that is bound to the actual types. | ||
33 | * | ||
34 | * @param domainClass | ||
35 | * Java type of the values that are being aggregated | ||
36 | * @return the actual run-time aggregator logic, with type bindings | ||
37 | */ | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass); | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java new file mode 100644 index 00000000..3bc22274 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java | |||
@@ -0,0 +1,106 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.psystem.aggregations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.aggregators.ExtremumOperator; | ||
15 | |||
16 | /** | ||
17 | * A single column aggregator is used to incrementally compute the aggregate of a multiset of values according to an aggregator operator. | ||
18 | * | ||
19 | * <p> The operator provides two methods of computation: <ul> | ||
20 | * <li>Stateless aggregation of an explicit multiset, provided by {@link #aggregateStatelessly(Collection)}.</li> | ||
21 | * <li>Incremental aggregation, provided by {@link #createNeutral()}, {@link #update(Object, Object, boolean)}, {@link #isNeutral(Object)}, {@link #getAggregate(Object)}. | ||
22 | * </ul> | ||
23 | * | ||
24 | * <p> In case of incremental computation, the aggregable multiset is conceptual; it is not represented by an explicit Collection<Domain> object, but its update operations are tracked. | ||
25 | * | ||
26 | * <p> In case of incremental computation, internal results, potentially distinct from the final aggregate result, may be stored in a helper data structure called <b>accumulator</b>. | ||
27 | * The goal of this distinction is that the final result may not be sufficient for incremental updates (see e.g. {@link ExtremumOperator}). | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | * | ||
31 | * @param <Domain> the type of elements to be aggregated. | ||
32 | * @param <Accumulator> the type used to store the interim results of the aggregate computation, | ||
33 | * that may be incrementally refreshed upon updates to the multiset, and that can easily yield the final result. | ||
34 | * @param <AggregateResult> the type of the final result of the aggregation to be output. | ||
35 | * | ||
36 | * @since 1.4 | ||
37 | */ | ||
38 | public interface IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> { | ||
39 | |||
40 | /** | ||
41 | * A textual description of the operator. | ||
42 | */ | ||
43 | String getShortDescription(); | ||
44 | |||
45 | /** | ||
46 | * A name or identifier of the operator. | ||
47 | */ | ||
48 | String getName(); | ||
49 | |||
50 | /** | ||
51 | * @return the neutral element, i.e. the interim result of aggregating an empty multiset. | ||
52 | */ | ||
53 | Accumulator createNeutral(); | ||
54 | |||
55 | /** | ||
56 | * @return true if the interim result is equivalent to the neutral element, as if there are no values in the multiset. | ||
57 | * Must return true if the multiset is empty. | ||
58 | */ | ||
59 | boolean isNeutral(Accumulator result); | ||
60 | |||
61 | /** | ||
62 | * @return an updated intermediate result, | ||
63 | * changed to reflect that a given object was added to / removed from the multiset | ||
64 | * (as indicated by the parameter isInsertion) | ||
65 | */ | ||
66 | Accumulator update(Accumulator oldResult, Domain updateValue, boolean isInsertion); | ||
67 | |||
68 | /** | ||
69 | * @return the aggregate result obtained from the given intermediate result. | ||
70 | * May be null to indicate that the current multiset cannot be aggregated (e.g. 0 elements have no minimum). | ||
71 | */ | ||
72 | AggregateResult getAggregate(Accumulator result); | ||
73 | |||
74 | /** | ||
75 | * Calculates the aggregate results from a given stream of values; all values are considered as inserted | ||
76 | * @return the aggregate result, or null if no result can be calculated (e.g. because of an empty stream) | ||
77 | * @since 2.0 | ||
78 | */ | ||
79 | AggregateResult aggregateStream(Stream<Domain> stream); | ||
80 | |||
81 | /** | ||
82 | * Clones the given accumulator (with all its internal contents). | ||
83 | */ | ||
84 | default Accumulator clone(Accumulator original) { | ||
85 | throw new UnsupportedOperationException(); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * Combines the given aggregate result and accumulator into a single aggregate result. | ||
90 | */ | ||
91 | default AggregateResult combine(AggregateResult left, Accumulator right) { | ||
92 | throw new UnsupportedOperationException(); | ||
93 | } | ||
94 | |||
95 | default boolean contains(Domain value, Accumulator accumulator) { | ||
96 | throw new UnsupportedOperationException(); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Pretty prints the contents of the given accumulator. | ||
101 | */ | ||
102 | default String prettyPrint(final Accumulator accumulator) { | ||
103 | return accumulator.toString(); | ||
104 | } | ||
105 | |||
106 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java new file mode 100644 index 00000000..e3f28cff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java | |||
@@ -0,0 +1,194 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs 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.psystem.analysis; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
30 | |||
31 | /** | ||
32 | * Object responsible for computing and caching static query analysis results. | ||
33 | * <p> Any client can instantiate this to statically analyze queries. | ||
34 | * Query backends should share an instance obtained via {@link IQueryBackendContext} to save resources. | ||
35 | * <p> Precondition: all involved queries must be initialized. | ||
36 | * @noinstantiate Considered unstable API; subject to change in future versions. | ||
37 | * Either use the analyzer provided by {@link IQueryBackendContext}, or anticipate | ||
38 | * potential future breakage when instantiating your own analyzer. | ||
39 | * @author Gabor Bergmann | ||
40 | * @since 1.5 | ||
41 | */ | ||
42 | public final class QueryAnalyzer { | ||
43 | |||
44 | private IQueryMetaContext metaContext; | ||
45 | |||
46 | public QueryAnalyzer(IQueryMetaContext metaContext) { | ||
47 | this.metaContext = metaContext; | ||
48 | } | ||
49 | |||
50 | // Functional dependencies | ||
51 | |||
52 | /** | ||
53 | * Maps query and strictness to functional dependencies | ||
54 | */ | ||
55 | private Map<PQuery, Map<Set<Integer>, Set<Integer>>> strictFunctionalDependencyGuarantees = | ||
56 | new HashMap<>(); | ||
57 | private Map<PQuery, Map<Set<Integer>, Set<Integer>>> softFunctionalDependencyGuarantees = | ||
58 | new HashMap<>(); | ||
59 | |||
60 | /** | ||
61 | * Functional dependency information, expressed on query parameters, that the match set of the query is guaranteed to respect. | ||
62 | * <p> The type dependencies shall be expressed on the <i>parameter index</i> integers, NOT the {@link PParameter} object. | ||
63 | * @return a non-null map of functional dependencies on parameters that can be processed by {@link FunctionalDependencyHelper} | ||
64 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
65 | * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
66 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
67 | * @since 1.5 | ||
68 | */ | ||
69 | public Map<Set<Integer>, Set<Integer>> getProjectedFunctionalDependencies(PQuery query, boolean strict) { | ||
70 | Map<PQuery, Map<Set<Integer>, Set<Integer>>> guaranteeStore = strict ? strictFunctionalDependencyGuarantees : softFunctionalDependencyGuarantees; | ||
71 | Map<Set<Integer>, Set<Integer>> dependencies = guaranteeStore.get(query); | ||
72 | // Why not computeIfAbsent? See Bug 532507 | ||
73 | // Invoked method #computeFunctionalDependencies may trigger functional dependency computation for called queries; | ||
74 | // and may thus recurs back into #getProjectedFunctionalDependencies, causing a ConcurrentModificationException | ||
75 | // if the called query has not been previously analyzed. | ||
76 | // | ||
77 | // Note: if patterns are recursive, the empty accumulator will be found in the store | ||
78 | // (this yields a safe lower estimate and guarantees termination for #getProjectedFunctionalDependencies) | ||
79 | // But this case probably will not occur due to recursive queries having a disjunction at some point, | ||
80 | // and thus ignored by #computeFunctionalDependencies | ||
81 | if (dependencies == null) { | ||
82 | dependencies = new HashMap<>(); // accumulator | ||
83 | guaranteeStore.put(query, dependencies); | ||
84 | computeFunctionalDependencies(dependencies, query, strict); | ||
85 | } | ||
86 | return dependencies; | ||
87 | } | ||
88 | |||
89 | private void computeFunctionalDependencies(Map<Set<Integer>, Set<Integer>> accumulator, PQuery query, boolean strict) { | ||
90 | Set<PBody> bodies = query.getDisjunctBodies().getBodies(); | ||
91 | if (bodies.size() == 1) { // no support for recursion or disjunction | ||
92 | |||
93 | PBody body = bodies.iterator().next(); | ||
94 | |||
95 | // collect parameter variables | ||
96 | Map<PVariable, Integer> parameters = body.getSymbolicParameters().stream() | ||
97 | .collect(Collectors.toMap(ExportedParameter::getParameterVariable, | ||
98 | param -> query.getParameters().indexOf(param.getPatternParameter()))); | ||
99 | |||
100 | // collect all internal dependencies | ||
101 | Map<Set<PVariable>, Set<PVariable>> internalDependencies = | ||
102 | getFunctionalDependencies(body.getConstraints(), strict); | ||
103 | |||
104 | // project onto parameter variables | ||
105 | Map<Set<PVariable>, Set<PVariable>> projectedDeps = | ||
106 | FunctionalDependencyHelper.projectDependencies(internalDependencies, parameters.keySet()); | ||
107 | |||
108 | // translate into indices | ||
109 | for (Entry<Set<PVariable>, Set<PVariable>> entry : projectedDeps.entrySet()) { | ||
110 | Set<Integer> left = new HashSet<Integer>(); | ||
111 | Set<Integer> right = new HashSet<Integer>(); | ||
112 | for (PVariable pVariable : entry.getKey()) { | ||
113 | left.add(parameters.get(pVariable)); | ||
114 | } | ||
115 | for (PVariable pVariable : entry.getValue()) { | ||
116 | right.add(parameters.get(pVariable)); | ||
117 | } | ||
118 | accumulator.put(left, right); | ||
119 | } | ||
120 | |||
121 | } else { | ||
122 | // Disjunctive case, no dependencies are inferred | ||
123 | // TODO: we can still salvage the intersection of dependencies IF | ||
124 | // - all bodies have disjoint match sets | ||
125 | // - and we avoid recursion | ||
126 | } | ||
127 | |||
128 | // add annotation-based soft dependencies (regardless of number of bodies) | ||
129 | if (!strict) { | ||
130 | outer: | ||
131 | for (PAnnotation annotation : query.getAnnotationsByName("FunctionalDependency")) { | ||
132 | Set<Integer> lefts = new HashSet<Integer>(); | ||
133 | Set<Integer> rights = new HashSet<Integer>(); | ||
134 | |||
135 | for (Object object : annotation.getAllValues("forEach")) { | ||
136 | ParameterReference parameter = (ParameterReference) object; | ||
137 | Integer position = query.getPositionOfParameter(parameter.getName()); | ||
138 | if (position == null) continue outer; | ||
139 | lefts.add(position); | ||
140 | } | ||
141 | for (Object object : annotation.getAllValues("unique")) { | ||
142 | ParameterReference parameter = (ParameterReference) object; | ||
143 | Integer position = query.getPositionOfParameter(parameter.getName()); | ||
144 | if (position == null) continue outer; | ||
145 | rights.add(position); | ||
146 | } | ||
147 | |||
148 | FunctionalDependencyHelper.includeDependency(accumulator, lefts, rights); | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * Functional dependency information, expressed on PVariables within a body, that the selected constraints imply. | ||
155 | * @return a non-null map of functional dependencies on PVariables that can be processed by {@link FunctionalDependencyHelper} | ||
156 | * @param constraints the set of constraints whose consequences will be analyzed | ||
157 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
158 | * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
159 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
160 | * @since 1.5 | ||
161 | */ | ||
162 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(Set<? extends PConstraint> constraints, boolean strict) { | ||
163 | Map<Set<PVariable>, Set<PVariable>> accumulator = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
164 | for (PConstraint pConstraint : constraints){ | ||
165 | if (pConstraint instanceof PositivePatternCall) { | ||
166 | // use query analysis results instead | ||
167 | PositivePatternCall call = (PositivePatternCall) pConstraint; | ||
168 | PQuery query = call.getSupplierKey(); | ||
169 | Map<Set<Integer>, Set<Integer>> paramDependencies = getProjectedFunctionalDependencies(query, strict); | ||
170 | for (Entry<Set<Integer>, Set<Integer>> entry : paramDependencies.entrySet()) { | ||
171 | Set<PVariable> lefts = new HashSet<PVariable>(); | ||
172 | Set<PVariable> rights = new HashSet<PVariable>(); | ||
173 | |||
174 | for (Integer index : entry.getKey()) { | ||
175 | lefts.add(call.getVariableInTuple(index)); | ||
176 | } | ||
177 | for (Integer index : entry.getValue()) { | ||
178 | rights.add(call.getVariableInTuple(index)); | ||
179 | } | ||
180 | |||
181 | FunctionalDependencyHelper.includeDependency(accumulator, | ||
182 | lefts, rights); | ||
183 | } | ||
184 | } else { | ||
185 | // delegate to PConstraint | ||
186 | FunctionalDependencyHelper.includeDependencies(accumulator, | ||
187 | pConstraint.getFunctionalDependencies(metaContext)); | ||
188 | } | ||
189 | } | ||
190 | return accumulator; | ||
191 | } | ||
192 | |||
193 | |||
194 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java new file mode 100644 index 00000000..c4fbe0e9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.annotations; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import org.eclipse.collections.api.multimap.MutableMultimap; | ||
17 | import org.eclipse.collections.impl.multimap.list.FastListMultimap; | ||
18 | |||
19 | /** | ||
20 | * A container describing query annotations | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * | ||
23 | */ | ||
24 | public class PAnnotation { | ||
25 | |||
26 | private final String name; | ||
27 | private MutableMultimap<String, Object> attributes = FastListMultimap.newMultimap(); | ||
28 | |||
29 | public PAnnotation(String name) { | ||
30 | this.name = name; | ||
31 | |||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Adds an attribute to the annotation | ||
36 | * @param attributeName | ||
37 | * @param value | ||
38 | */ | ||
39 | public void addAttribute(String attributeName, Object value) { | ||
40 | attributes.put(attributeName, value); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Return the name of the annotation | ||
45 | */ | ||
46 | public String getName() { | ||
47 | return name; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Returns the value of the first occurrence of an attribute | ||
52 | * @param attributeName | ||
53 | * @return the attribute value, or null, if attribute is not available | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | public Optional<Object> getFirstValue(String attributeName) { | ||
57 | return getAllValues(attributeName).stream().findFirst(); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Returns the value of the first occurrence of an attribute | ||
62 | * @param attributeName | ||
63 | * @return the attribute value, or null, if attribute is not available | ||
64 | * @since 2.0 | ||
65 | */ | ||
66 | public <T> Optional<T> getFirstValue(String attributeName, Class<T> clazz) { | ||
67 | return getAllValues(attributeName).stream().filter(clazz::isInstance).map(clazz::cast).findFirst(); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Returns all values of a selected attribute | ||
72 | * @param attributeName | ||
73 | * @return a non-null, but possibly empty list of attributes | ||
74 | */ | ||
75 | public List<Object> getAllValues(String attributeName) { | ||
76 | return attributes.get(attributeName).toList(); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Executes a consumer over all attributes. A selected attribute name (key) can appear (and thus consumed) multiple times. | ||
81 | * @since 2.0 | ||
82 | */ | ||
83 | public void forEachValue(BiConsumer<String, Object> consumer) { | ||
84 | attributes.forEachKeyValue(consumer::accept); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Returns a set of all attribute names used in this annotation | ||
89 | * @since 2.1 | ||
90 | */ | ||
91 | public Set<String> getAllAttributeNames() { | ||
92 | return attributes.keySet().toSet(); | ||
93 | } | ||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java new file mode 100644 index 00000000..c67e9046 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.annotations; | ||
10 | |||
11 | /** | ||
12 | * An annotation parameter referencing a query parameter by name. Does not check whether the parameter exists. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * | ||
16 | */ | ||
17 | public class ParameterReference { | ||
18 | |||
19 | final String name; | ||
20 | |||
21 | public ParameterReference(String name) { | ||
22 | super(); | ||
23 | this.name = name; | ||
24 | } | ||
25 | |||
26 | public String getName() { | ||
27 | return name; | ||
28 | } | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java new file mode 100644 index 00000000..56f86e89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
25 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
26 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
27 | |||
28 | /** | ||
29 | * The PSystem representation of an aggregation. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 1.4 | ||
33 | */ | ||
34 | public class AggregatorConstraint extends PatternCallBasedDeferred implements ITypeInfoProviderConstraint { | ||
35 | |||
36 | protected PVariable resultVariable; | ||
37 | private BoundAggregator aggregator; | ||
38 | protected int aggregatedColumn; | ||
39 | |||
40 | public AggregatorConstraint(BoundAggregator aggregator, PBody pBody, Tuple actualParametersTuple, PQuery query, | ||
41 | PVariable resultVariable, int aggregatedColumn) { | ||
42 | super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); | ||
43 | this.resultVariable = resultVariable; | ||
44 | this.aggregatedColumn = aggregatedColumn; | ||
45 | this.aggregator = aggregator; | ||
46 | } | ||
47 | |||
48 | public int getAggregatedColumn() { | ||
49 | return this.aggregatedColumn; | ||
50 | } | ||
51 | |||
52 | public BoundAggregator getAggregator() { | ||
53 | return this.aggregator; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Set<PVariable> getDeducedVariables() { | ||
58 | return Collections.singleton(resultVariable); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
63 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
64 | result.put(getDeferringVariables(), getDeducedVariables()); | ||
65 | return result; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
70 | if (resultVariable.equals(obsolete)) | ||
71 | resultVariable = replacement; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
76 | return actualParametersTuple.<PVariable> getDistinctElements(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | protected String toStringRest() { | ||
81 | return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" | ||
82 | + resultVariable.toString(); | ||
83 | } | ||
84 | |||
85 | public PVariable getResultVariable() { | ||
86 | return resultVariable; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
91 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
92 | IInputKey aggregateResultType = aggregator.getAggregateResultTypeAsInputKey(); | ||
93 | if (aggregateResultType != null) { | ||
94 | result.add(new TypeJudgement(aggregateResultType, Tuples.staticArityFlatTupleOf(resultVariable))); | ||
95 | } | ||
96 | return result; | ||
97 | } | ||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java new file mode 100644 index 00000000..7bc949a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java | |||
@@ -0,0 +1,99 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public abstract class BaseTypeSafeConstraint extends | ||
29 | VariableDeferredPConstraint { | ||
30 | |||
31 | protected Set<PVariable> inputVariables; | ||
32 | protected PVariable outputVariable; | ||
33 | |||
34 | public PVariable getOutputVariable() { | ||
35 | return outputVariable; | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * @param pBody | ||
40 | * @param inputVariables | ||
41 | * @param outputVariable null iff no output (check-only) | ||
42 | */ | ||
43 | public BaseTypeSafeConstraint(PBody pBody, | ||
44 | Set<PVariable> inputVariables, final PVariable outputVariable) { | ||
45 | super(pBody, | ||
46 | (outputVariable == null) ? | ||
47 | inputVariables : | ||
48 | Stream.concat(inputVariables.stream(), Stream.of(outputVariable)).collect(Collectors.toSet()) | ||
49 | ); | ||
50 | this.inputVariables = inputVariables; | ||
51 | this.outputVariable = outputVariable; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Set<PVariable> getDeducedVariables() { | ||
56 | if (outputVariable == null) | ||
57 | return Collections.emptySet(); | ||
58 | else | ||
59 | return Collections.singleton(outputVariable); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Set<PVariable> getDeferringVariables() { | ||
64 | return inputVariables; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
69 | if (super.isReadyAt(plan, context)) { | ||
70 | return checkTypeSafety(plan, context) == null; | ||
71 | } | ||
72 | return false; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Checks whether all type restrictions are already enforced on affected variables. | ||
77 | * | ||
78 | * @param plan | ||
79 | * @return a variable whose type safety is not enforced yet, or null if the plan is typesafe | ||
80 | */ | ||
81 | public PVariable checkTypeSafety(SubPlan plan, IQueryMetaContext context) { | ||
82 | Set<TypeJudgement> impliedJudgements = plan.getAllImpliedTypeJudgements(context); | ||
83 | |||
84 | for (PVariable pVariable : inputVariables) { | ||
85 | Set<TypeJudgement> allTypeRestrictionsForVariable = pBody.getAllUnaryTypeRestrictions(context).get(pVariable); | ||
86 | if (allTypeRestrictionsForVariable != null && !impliedJudgements.containsAll(allTypeRestrictionsForVariable)) | ||
87 | return pVariable; | ||
88 | } | ||
89 | return null; | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
94 | if (inputVariables.remove(obsolete)) | ||
95 | inputVariables.add(replacement); | ||
96 | if (outputVariable == obsolete) | ||
97 | outputVariable = replacement; | ||
98 | } | ||
99 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java new file mode 100644 index 00000000..b978b62c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java | |||
@@ -0,0 +1,96 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public class Equality extends DeferredPConstraint { | ||
29 | |||
30 | private PVariable who; | ||
31 | private PVariable withWhom; | ||
32 | |||
33 | public Equality(PBody pBody, PVariable who, PVariable withWhom) { | ||
34 | super(pBody, buildSet(who, withWhom)); | ||
35 | this.who = who; | ||
36 | this.withWhom = withWhom; | ||
37 | } | ||
38 | |||
39 | private static Set<PVariable> buildSet(PVariable who, PVariable withWhom) { | ||
40 | Set<PVariable> set = new HashSet<PVariable>(); | ||
41 | set.add(who); | ||
42 | set.add(withWhom); | ||
43 | return set; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * An equality is moot if it compares the a variable with itself. | ||
48 | * | ||
49 | * @return true, if the equality is moot | ||
50 | */ | ||
51 | public boolean isMoot() { | ||
52 | return who.equals(withWhom); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
57 | if (obsolete.equals(who)) | ||
58 | who = replacement; | ||
59 | if (obsolete.equals(withWhom)) | ||
60 | withWhom = replacement; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | protected String toStringRest() { | ||
65 | return who.getName() + "=" + withWhom.getName(); | ||
66 | } | ||
67 | |||
68 | public PVariable getWho() { | ||
69 | return who; | ||
70 | } | ||
71 | |||
72 | public PVariable getWithWhom() { | ||
73 | return withWhom; | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public Set<PVariable> getDeducedVariables() { | ||
78 | return Collections.emptySet(); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
83 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
84 | result.put(Collections.singleton(who), Collections.singleton(withWhom)); | ||
85 | result.put(Collections.singleton(withWhom), Collections.singleton(who)); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
91 | return plan.getVisibleVariables().contains(who) && plan.getVisibleVariables().contains(withWhom); | ||
92 | // will be replaced by || if copierNode is available; | ||
93 | // until then, LayoutHelper.unifyVariablesAlongEqualities(PSystem<PatternDescription, StubHandle, Collector>) is | ||
94 | // recommended. | ||
95 | } | ||
96 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java new file mode 100644 index 00000000..80706792 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | |||
22 | /** | ||
23 | * @author Gabor Bergmann | ||
24 | * | ||
25 | */ | ||
26 | public class ExportedParameter extends VariableDeferredPConstraint { | ||
27 | PVariable parameterVariable; | ||
28 | final String parameterName; | ||
29 | final PParameter patternParameter; | ||
30 | |||
31 | /** | ||
32 | * @since 1.4 | ||
33 | */ | ||
34 | public ExportedParameter(PBody pBody, PVariable parameterVariable, PParameter patternParameter) { | ||
35 | super(pBody, Collections.singleton(parameterVariable)); | ||
36 | this.parameterVariable = parameterVariable; | ||
37 | this.patternParameter = patternParameter; | ||
38 | parameterName = patternParameter.getName(); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
43 | if (obsolete.equals(parameterVariable)) | ||
44 | parameterVariable = replacement; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected String toStringRest() { | ||
49 | Object varName = parameterVariable.getName(); | ||
50 | return parameterName.equals(varName) ? parameterName : parameterName + "(" + varName + ")"; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<PVariable> getDeducedVariables() { | ||
55 | return Collections.emptySet(); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * The name of the parameter; usually, it is expected that {@link #getParameterVariable()} is more useful, except | ||
60 | * maybe for debugging purposes. | ||
61 | * | ||
62 | * @return a non-null name of the parameter | ||
63 | */ | ||
64 | public String getParameterName() { | ||
65 | return parameterName; | ||
66 | } | ||
67 | |||
68 | public PVariable getParameterVariable() { | ||
69 | return parameterVariable; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * @since 1.4 | ||
74 | */ | ||
75 | public PParameter getPatternParameter() { | ||
76 | if (patternParameter == null) { | ||
77 | PQuery query = pBody.getPattern(); | ||
78 | Integer index = query.getPositionOfParameter(parameterName); | ||
79 | if (index == null) { | ||
80 | throw new IllegalStateException(String.format("Pattern %s does not have a parameter named %s", | ||
81 | query.getFullyQualifiedName(), parameterName)); | ||
82 | } | ||
83 | return query.getParameters().get(index); | ||
84 | } else { | ||
85 | return patternParameter; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Set<PVariable> getDeferringVariables() { | ||
91 | return Collections.singleton(parameterVariable); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public void checkSanity() { | ||
96 | super.checkSanity(); | ||
97 | if (!parameterVariable.isDeducable()) { | ||
98 | String[] args = { parameterName }; | ||
99 | String msg = "Impossible to match pattern: " | ||
100 | + "exported pattern variable {1} can not be determined based on the pattern constraints. " | ||
101 | + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; | ||
102 | String shortMsg = "Could not deduce value of parameter"; | ||
103 | throw new QueryProcessingException(msg, args, shortMsg, null); | ||
104 | } | ||
105 | |||
106 | } | ||
107 | |||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java new file mode 100644 index 00000000..06688c36 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java | |||
@@ -0,0 +1,80 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.LinkedHashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * | ||
26 | */ | ||
27 | public class ExpressionEvaluation extends BaseTypeSafeConstraint { | ||
28 | |||
29 | private IExpressionEvaluator evaluator; | ||
30 | private boolean isUnwinding; | ||
31 | |||
32 | public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable) { | ||
33 | this(pBody, evaluator, outputVariable, false); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 2.4 | ||
38 | */ | ||
39 | public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable, | ||
40 | boolean isUnwinding) { | ||
41 | super(pBody, getPVariablesOfExpression(pBody, evaluator), outputVariable); | ||
42 | this.evaluator = evaluator; | ||
43 | this.isUnwinding = isUnwinding; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * @since 2.4 | ||
48 | */ | ||
49 | public boolean isUnwinding() { | ||
50 | return isUnwinding; | ||
51 | } | ||
52 | |||
53 | public IExpressionEvaluator getEvaluator() { | ||
54 | return evaluator; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | protected String toStringRest() { | ||
59 | return Tuples.flatTupleOf(new ArrayList<PVariable>(inputVariables).toArray()).toString() + "|=" | ||
60 | + evaluator.getShortDescription(); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
65 | if (outputVariable == null) | ||
66 | return Collections.emptyMap(); | ||
67 | else | ||
68 | return Collections.singletonMap(inputVariables, Collections.singleton(outputVariable)); | ||
69 | } | ||
70 | |||
71 | private static Set<PVariable> getPVariablesOfExpression(PBody pBody, IExpressionEvaluator evaluator) { | ||
72 | // use a linked set, so that the variables will come in the order of the parameters | ||
73 | Set<PVariable> result = new LinkedHashSet<PVariable>(); | ||
74 | for (String name : evaluator.getInputParameterNames()) { | ||
75 | PVariable variable = pBody.getOrCreateVariableByName(name); | ||
76 | result.add(variable); | ||
77 | } | ||
78 | return result; | ||
79 | } | ||
80 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java new file mode 100644 index 00000000..5cac33dc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java | |||
@@ -0,0 +1,151 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
20 | |||
21 | /** | ||
22 | * @author Gabor Bergmann | ||
23 | * | ||
24 | */ | ||
25 | public class Inequality extends VariableDeferredPConstraint { | ||
26 | |||
27 | private PVariable who; | ||
28 | private PVariable withWhom; | ||
29 | |||
30 | /** | ||
31 | * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is | ||
32 | * undeducible. | ||
33 | */ | ||
34 | private boolean weak; | ||
35 | |||
36 | public Inequality(PBody pBody, PVariable who, PVariable withWhom) { | ||
37 | this(pBody, who, withWhom, false); | ||
38 | } | ||
39 | |||
40 | public Inequality(PBody pBody, PVariable who, PVariable withWhom, | ||
41 | boolean weak) { | ||
42 | super(pBody, new HashSet<>(Arrays.asList(who, withWhom) )); | ||
43 | this.who = who; | ||
44 | this.withWhom = withWhom; | ||
45 | this.weak = weak; | ||
46 | } | ||
47 | |||
48 | // private Inequality( | ||
49 | // PSystem<PatternDescription, StubHandle, ?> pSystem, | ||
50 | // PVariable subject, Set<PVariable> inequals) | ||
51 | // { | ||
52 | // super(pSystem, include(inequals, subject)); | ||
53 | // this.subject = subject; | ||
54 | // this.inequals = inequals; | ||
55 | // } | ||
56 | |||
57 | // private static HashSet<PVariable> include(Set<PVariable> inequals, PVariable subject) { | ||
58 | // HashSet<PVariable> hashSet = new HashSet<PVariable>(inequals); | ||
59 | // hashSet.add(subject); | ||
60 | // return hashSet; | ||
61 | // } | ||
62 | |||
63 | @Override | ||
64 | public Set<PVariable> getDeferringVariables() { | ||
65 | return getAffectedVariables(); | ||
66 | } | ||
67 | |||
68 | // private static int[] mapIndices(Map<Object, Integer> variablesIndex, Set<PVariable> keys) { | ||
69 | // int[] result = new int[keys.size()]; | ||
70 | // int k = 0; | ||
71 | // for (PVariable key : keys) { | ||
72 | // result[k++] = variablesIndex.get(key); | ||
73 | // } | ||
74 | // return result; | ||
75 | // } | ||
76 | |||
77 | // @Override | ||
78 | // public IFoldablePConstraint getIncorporator() { | ||
79 | // return incorporator; | ||
80 | // } | ||
81 | // | ||
82 | // @Override | ||
83 | // public void registerIncorporatationInto(IFoldablePConstraint incorporator) { | ||
84 | // this.incorporator = incorporator; | ||
85 | // } | ||
86 | // | ||
87 | // @Override | ||
88 | // public boolean incorporate(IFoldablePConstraint other) { | ||
89 | // if (other instanceof Inequality<?, ?>) { | ||
90 | // Inequality other2 = (Inequality) other; | ||
91 | // if (subject.equals(other2.subject)) { | ||
92 | // Set<PVariable> newInequals = new HashSet<PVariable>(inequals); | ||
93 | // newInequals.addAll(other2.inequals); | ||
94 | // return new Inequality<PatternDescription, StubHandle>(buildable, subject, newInequals); | ||
95 | // } | ||
96 | // } else return false; | ||
97 | // } | ||
98 | |||
99 | @Override | ||
100 | protected String toStringRest() { | ||
101 | return who.toString() + (isWeak() ? "!=?" : "!=") + withWhom.toString(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
106 | if (obsolete.equals(who)) | ||
107 | who = replacement; | ||
108 | if (obsolete.equals(withWhom)) | ||
109 | withWhom = replacement; | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public Set<PVariable> getDeducedVariables() { | ||
114 | return Collections.emptySet(); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is | ||
119 | * undeducible. | ||
120 | * | ||
121 | * @return the weak | ||
122 | */ | ||
123 | public boolean isWeak() { | ||
124 | return weak; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * A weak inequality constraint is eliminable if who is the same as withWhom, or if any if them is undeducible. | ||
129 | */ | ||
130 | public boolean isEliminable() { | ||
131 | return isWeak() && (who.equals(withWhom) || !who.isDeducable() || !withWhom.isDeducable()); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Eliminates a weak inequality constraint if it can be ignored when who is the same as withWhom, or if any if them | ||
136 | * is undeducible. | ||
137 | */ | ||
138 | public void eliminateWeak() { | ||
139 | if (isEliminable()) | ||
140 | delete(); | ||
141 | } | ||
142 | |||
143 | public PVariable getWho() { | ||
144 | return who; | ||
145 | } | ||
146 | |||
147 | public PVariable getWithWhom() { | ||
148 | return withWhom; | ||
149 | } | ||
150 | |||
151 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java new file mode 100644 index 00000000..87d9d9fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public class NegativePatternCall extends PatternCallBasedDeferred { | ||
25 | |||
26 | public NegativePatternCall(PBody pBody, Tuple actualParametersTuple, PQuery query) { | ||
27 | super(pBody, actualParametersTuple, query); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Set<PVariable> getDeducedVariables() { | ||
32 | return Collections.emptySet(); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @return all variables that may potentially be quantified they are not used anywhere else | ||
37 | */ | ||
38 | @Override | ||
39 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
40 | return getAffectedVariables(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected String toStringRest() { | ||
49 | return "!" + query.getFullyQualifiedName() + "@" + actualParametersTuple.toString(); | ||
50 | } | ||
51 | |||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java new file mode 100644 index 00000000..93eeffec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java | |||
@@ -0,0 +1,118 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | |||
25 | /** | ||
26 | * @author Gabor Bergmann | ||
27 | * | ||
28 | */ | ||
29 | public abstract class PatternCallBasedDeferred extends VariableDeferredPConstraint implements IQueryReference { | ||
30 | |||
31 | protected Tuple actualParametersTuple; | ||
32 | |||
33 | protected abstract void doDoReplaceVariables(PVariable obsolete, PVariable replacement); | ||
34 | |||
35 | protected abstract Set<PVariable> getCandidateQuantifiedVariables(); | ||
36 | |||
37 | protected PQuery query; | ||
38 | private Set<PVariable> deferringVariables; | ||
39 | |||
40 | public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, | ||
41 | PQuery pattern, Set<PVariable> additionalAffectedVariables) { | ||
42 | super(pBody, union(actualParametersTuple.<PVariable> getDistinctElements(), additionalAffectedVariables)); | ||
43 | this.actualParametersTuple = actualParametersTuple; | ||
44 | this.query = pattern; | ||
45 | } | ||
46 | |||
47 | public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, | ||
48 | PQuery pattern) { | ||
49 | this(pBody, actualParametersTuple, pattern, Collections.<PVariable> emptySet()); | ||
50 | } | ||
51 | |||
52 | private static Set<PVariable> union(Set<PVariable> a, Set<PVariable> b) { | ||
53 | Set<PVariable> result = new HashSet<PVariable>(); | ||
54 | result.addAll(a); | ||
55 | result.addAll(b); | ||
56 | return result; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Set<PVariable> getDeferringVariables() { | ||
61 | if (deferringVariables == null) { | ||
62 | deferringVariables = new HashSet<PVariable>(); | ||
63 | for (PVariable var : getCandidateQuantifiedVariables()) { | ||
64 | if (var.isDeducable()) | ||
65 | deferringVariables.add(var); | ||
66 | } | ||
67 | } | ||
68 | return deferringVariables; | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public void checkSanity() { | ||
73 | super.checkSanity(); | ||
74 | for (Object obj : this.actualParametersTuple.getDistinctElements()) { | ||
75 | PVariable var = (PVariable) obj; | ||
76 | if (!getDeferringVariables().contains(var)) { | ||
77 | // so this is a free variable of the NAC / aggregation? | ||
78 | for (PConstraint pConstraint : var.getReferringConstraints()) { | ||
79 | if (pConstraint != this | ||
80 | && !(pConstraint instanceof Equality && ((Equality) pConstraint).isMoot())) | ||
81 | throw new QueryProcessingException ( | ||
82 | "Variable {1} of constraint {2} is not a positively determined part of the pattern, yet it is also affected by {3}.", | ||
83 | new String[] { var.toString(), this.toString(), pConstraint.toString() }, | ||
84 | "Read-only variable can not be deduced", null); | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | // public SubPlan getSidePlan(IOperationCompiler compiler) throws QueryPlannerException { | ||
92 | // SubPlan sidePlan = compiler.patternCallPlan(actualParametersTuple, query); | ||
93 | // sidePlan = BuildHelper.enforceVariableCoincidences(compiler, sidePlan); | ||
94 | // return sidePlan; | ||
95 | // } | ||
96 | |||
97 | @Override | ||
98 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
99 | if (deferringVariables != null) { | ||
100 | // FAIL instead of hopeless attempt to fix | ||
101 | // if (deferringVariables.remove(obsolete)) deferringVariables.add(replacement); | ||
102 | throw new IllegalStateException("Cannot replace variables on " + this | ||
103 | + " when deferring variables have already been identified."); | ||
104 | } | ||
105 | actualParametersTuple = actualParametersTuple.replaceAll(obsolete, replacement); | ||
106 | doDoReplaceVariables(obsolete, replacement); | ||
107 | } | ||
108 | |||
109 | public Tuple getActualParametersTuple() { | ||
110 | return actualParametersTuple; | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public PQuery getReferredQuery() { | ||
115 | return query; | ||
116 | } | ||
117 | |||
118 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java new file mode 100644 index 00000000..0c40d91e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | */ | ||
26 | public class PatternMatchCounter extends PatternCallBasedDeferred { | ||
27 | |||
28 | private PVariable resultVariable; | ||
29 | |||
30 | public PatternMatchCounter(PBody pBody, Tuple actualParametersTuple, | ||
31 | PQuery query, PVariable resultVariable) { | ||
32 | super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); | ||
33 | this.resultVariable = resultVariable; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Set<PVariable> getDeducedVariables() { | ||
38 | return Collections.singleton(resultVariable); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
43 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
44 | result.put(getDeferringVariables(), getDeducedVariables()); | ||
45 | return result; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
50 | if (resultVariable.equals(obsolete)) | ||
51 | resultVariable = replacement; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
56 | return actualParametersTuple.<PVariable> getDistinctElements(); | ||
57 | } | ||
58 | |||
59 | |||
60 | @Override | ||
61 | protected String toStringRest() { | ||
62 | return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" | ||
63 | + resultVariable.toString(); | ||
64 | } | ||
65 | |||
66 | public PVariable getResultVariable() { | ||
67 | return resultVariable; | ||
68 | } | ||
69 | |||
70 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java new file mode 100644 index 00000000..336a83fb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | /** | ||
21 | * A constraint which prescribes the evaluation of custom Java logic that takes an arbitrary number of input relations | ||
22 | * and produces one output relation. Contrast this to {@link ExpressionEvaluation}, which produces a single output value | ||
23 | * given an input tuple. | ||
24 | * | ||
25 | * The assumption is that the relation evaluation logic is not incremental, that is, it can only perform from-scratch | ||
26 | * computation of the output relation given the complete input relations. To this end, the relation evaluator always | ||
27 | * receives the complete input relations with all their contents as input. However, the evaluator engine makes sure that | ||
28 | * the output of the relation evaluation is at least "seemingly" incremental. This means that the underlying computation | ||
29 | * network computes the delta on the output compared to the previous output and only propagates the delta further. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * | ||
33 | * @since 2.8 | ||
34 | * | ||
35 | */ | ||
36 | public class RelationEvaluation extends EnumerablePConstraint implements IMultiQueryReference { | ||
37 | |||
38 | private final IRelationEvaluator evaluator; | ||
39 | private final List<PQuery> inputQueries; | ||
40 | |||
41 | public RelationEvaluation(final PBody body, final Tuple variablesTuple, final List<PQuery> inputQueries, | ||
42 | final IRelationEvaluator evaluator) { | ||
43 | super(body, variablesTuple); | ||
44 | this.evaluator = evaluator; | ||
45 | this.inputQueries = inputQueries; | ||
46 | } | ||
47 | |||
48 | public IRelationEvaluator getEvaluator() { | ||
49 | return this.evaluator; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public List<PQuery> getReferredQueries() { | ||
54 | return this.inputQueries; | ||
55 | } | ||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java new file mode 100644 index 00000000..8b6e29ef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
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.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | |||
25 | /** | ||
26 | * Represents a non-enumerable type constraint that asserts that values substituted for the given tuple of variables | ||
27 | * form a tuple that belongs to a (typically non-enumerable) extensional relation identified by an {@link IInputKey}. | ||
28 | * | ||
29 | * <p> The InputKey is typically not enumerable. If it is enumerable, use {@link TypeConstraint} instead, so that the PConstraint carries over the property of enumerability. | ||
30 | * | ||
31 | * @author Bergmann Gabor | ||
32 | * | ||
33 | */ | ||
34 | public class TypeFilterConstraint extends VariableDeferredPConstraint implements | ||
35 | ITypeConstraint { | ||
36 | |||
37 | private Tuple variablesTuple; | ||
38 | private IInputKey inputKey; | ||
39 | |||
40 | private TypeJudgement equivalentJudgement; | ||
41 | |||
42 | |||
43 | public TypeFilterConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { | ||
44 | super(pBody, variablesTuple.<PVariable> getDistinctElements()); | ||
45 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
46 | |||
47 | this.variablesTuple = variablesTuple; | ||
48 | this.inputKey = inputKey; | ||
49 | |||
50 | if (variablesTuple.getSize() != inputKey.getArity()) | ||
51 | throw new IllegalArgumentException( | ||
52 | this.getClass().getSimpleName() + | ||
53 | " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + | ||
54 | inputKey); | ||
55 | } | ||
56 | |||
57 | |||
58 | |||
59 | public Tuple getVariablesTuple() { | ||
60 | return variablesTuple; | ||
61 | } | ||
62 | |||
63 | public IInputKey getInputKey() { | ||
64 | return inputKey; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public TypeJudgement getEquivalentJudgement() { | ||
69 | return equivalentJudgement; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
74 | variablesTuple = variablesTuple.replaceAll(obsolete, replacement); | ||
75 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
80 | return Collections.singleton(equivalentJudgement); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Set<PVariable> getDeducedVariables() { | ||
85 | return Collections.emptySet(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public Set<PVariable> getDeferringVariables() { | ||
90 | return getAffectedVariables(); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | protected String toStringRest() { | ||
95 | return inputKey.getPrettyPrintableName() + "@" + variablesTuple; | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
100 | return TypeConstraintUtil.getFunctionalDependencies(context, inputKey, variablesTuple); | ||
101 | } | ||
102 | |||
103 | |||
104 | |||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java new file mode 100644 index 00000000..7bbf7118 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, Zoltan Ujhelyi, 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.psystem.basicenumerables; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | |||
22 | /** | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | public abstract class AbstractTransitiveClosure extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint { | ||
26 | |||
27 | public AbstractTransitiveClosure(PBody pBody, Tuple variablesTuple, PQuery supplierKey) { | ||
28 | super(pBody, variablesTuple, supplierKey); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public PQuery getReferredQuery() { | ||
33 | return supplierKey; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 1.3 | ||
38 | */ | ||
39 | @Override | ||
40 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
41 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
42 | } | ||
43 | |||
44 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java new file mode 100644 index 00000000..e3dae240 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Zoltan Ujhelyi, 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.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
13 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | |||
18 | /** | ||
19 | * For a binary base pattern over an enumerable universe type, computes the reflexive transitive closure (base)* | ||
20 | * | ||
21 | * @author Gabor Bergmann, Zoltan Ujhelyi | ||
22 | * @since 2.0 | ||
23 | */ | ||
24 | public class BinaryReflexiveTransitiveClosure extends AbstractTransitiveClosure { | ||
25 | |||
26 | private final IInputKey universeType; | ||
27 | |||
28 | public BinaryReflexiveTransitiveClosure(PBody pBody, Tuple variablesTuple, | ||
29 | PQuery pattern, IInputKey universeType) { | ||
30 | super(pBody, variablesTuple, pattern); | ||
31 | this.universeType = universeType; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | protected String keyToString() { | ||
36 | return supplierKey.getFullyQualifiedName() + "*"; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Returns the type whose instances should be returned as 0-long paths. | ||
41 | * @since 2.0 | ||
42 | */ | ||
43 | public IInputKey getUniverseType() { | ||
44 | return universeType; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public void checkSanity() { | ||
49 | if (!universeType.isEnumerable() || universeType.getArity() != 1) { | ||
50 | throw new QueryProcessingException( | ||
51 | String.format("Invalid universe type %s - it should be enumerable and must have an arity of 1.", | ||
52 | universeType.getPrettyPrintableName()), | ||
53 | pBody.getPattern()); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java new file mode 100644 index 00000000..716d043b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * For a binary base pattern, computes the irreflexive transitive closure (base)+ | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public class BinaryTransitiveClosure extends AbstractTransitiveClosure { | ||
23 | |||
24 | public BinaryTransitiveClosure(PBody pBody, Tuple variablesTuple, | ||
25 | PQuery pattern) { | ||
26 | super(pBody, variablesTuple, pattern); | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | protected String keyToString() { | ||
31 | return supplierKey.getFullyQualifiedName() + "+"; | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java new file mode 100644 index 00000000..10da2e21 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | public enum Connectivity { | ||
9 | WEAK, | ||
10 | STRONG; | ||
11 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java new file mode 100644 index 00000000..251146c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | * | ||
26 | */ | ||
27 | public class ConstantValue extends KeyedEnumerablePConstraint<Object> { | ||
28 | |||
29 | private PVariable variable; | ||
30 | |||
31 | public ConstantValue(PBody pBody, PVariable variable, Object value) { | ||
32 | super(pBody, Tuples.staticArityFlatTupleOf(variable), value); | ||
33 | this.variable = variable; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | protected String keyToString() { | ||
38 | return supplierKey.toString(); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @since 1.7 | ||
43 | */ | ||
44 | public PVariable getVariable() { | ||
45 | return variable; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
50 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
51 | final Set<PVariable> emptySet = Collections.emptySet(); // a constant value is functionally determined by everything | ||
52 | result.put(emptySet, Collections.singleton(getVariableInTuple(0))); | ||
53 | return result; | ||
54 | } | ||
55 | |||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java new file mode 100644 index 00000000..25ab34b4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
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.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | */ | ||
30 | public class PositivePatternCall extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint { | ||
31 | |||
32 | public PositivePatternCall(PBody pBody, Tuple variablesTuple, | ||
33 | PQuery pattern) { | ||
34 | super(pBody, variablesTuple, pattern); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | protected String keyToString() { | ||
39 | return supplierKey.getFullyQualifiedName(); | ||
40 | } | ||
41 | |||
42 | // Note: #getFunctionalDependencies is intentionally not implemented - use QueryAnalyzer instead! | ||
43 | // @Override | ||
44 | // public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
45 | // return super.getFunctionalDependencies(context); | ||
46 | // } | ||
47 | |||
48 | @Override | ||
49 | public PQuery getReferredQuery() { | ||
50 | return supplierKey; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
55 | return getTypesImpliedByCall(supplierKey, variablesTuple); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * @since 1.3 | ||
60 | */ | ||
61 | public static Set<TypeJudgement> getTypesImpliedByCall(PQuery calledQuery, Tuple actualParametersTuple) { | ||
62 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
63 | for (TypeJudgement parameterJudgement : calledQuery.getTypeGuarantees()) { | ||
64 | IInputKey inputKey = parameterJudgement.getInputKey(); | ||
65 | Tuple judgementIndexTuple = parameterJudgement.getVariablesTuple(); | ||
66 | |||
67 | Object[] judgementVariables = new Object[judgementIndexTuple.getSize()]; | ||
68 | for (int i=0; i<judgementVariables.length; ++i) | ||
69 | judgementVariables[i] = actualParametersTuple.get((int) judgementIndexTuple.get(i)); | ||
70 | |||
71 | result.add(new TypeJudgement(inputKey, Tuples.flatTupleOf(judgementVariables))); | ||
72 | } | ||
73 | return result; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java new file mode 100644 index 00000000..b97ff55f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
9 | import tools.refinery.viatra.runtime.matchers.psystem.*; | ||
10 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | import java.util.Set; | ||
14 | |||
15 | public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint<PQuery> | ||
16 | implements IQueryReference, ITypeInfoProviderConstraint { | ||
17 | private final Connectivity connectivity; | ||
18 | |||
19 | public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, | ||
20 | Connectivity connectivity) { | ||
21 | super(pBody, variablesTuple, supplierKey); | ||
22 | this.connectivity = connectivity; | ||
23 | } | ||
24 | |||
25 | public Connectivity getConnectivity() { | ||
26 | return connectivity; | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public PQuery getReferredQuery() { | ||
31 | return supplierKey; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
36 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected String keyToString() { | ||
41 | return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative"; | ||
42 | } | ||
43 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java new file mode 100644 index 00000000..2ca54cc0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java | |||
@@ -0,0 +1,79 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.basicenumerables; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
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.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
23 | |||
24 | /** | ||
25 | * Represents an enumerable type constraint that asserts that values substituted for the given tuple of variables | ||
26 | * form a tuple that belongs to an enumerable extensional relation identified by an {@link IInputKey}. | ||
27 | * | ||
28 | * <p> The InputKey must be enumerable! | ||
29 | * | ||
30 | * @author Zoltan Ujhelyi | ||
31 | * | ||
32 | */ | ||
33 | public class TypeConstraint extends KeyedEnumerablePConstraint<IInputKey> implements ITypeConstraint { | ||
34 | |||
35 | private TypeJudgement equivalentJudgement; | ||
36 | |||
37 | public TypeConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { | ||
38 | super(pBody, variablesTuple, inputKey); | ||
39 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
40 | |||
41 | if (! inputKey.isEnumerable()) | ||
42 | throw new IllegalArgumentException( | ||
43 | this.getClass().getSimpleName() + | ||
44 | " applicable for enumerable input keys only; received instead " + | ||
45 | inputKey); | ||
46 | if (variablesTuple.getSize() != inputKey.getArity()) | ||
47 | throw new IllegalArgumentException( | ||
48 | this.getClass().getSimpleName() + | ||
49 | " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + | ||
50 | inputKey); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | protected String keyToString() { | ||
55 | return supplierKey.getPrettyPrintableName(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public TypeJudgement getEquivalentJudgement() { | ||
60 | return equivalentJudgement; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
65 | return Collections.singleton(equivalentJudgement); | ||
66 | //return equivalentJudgement.getDirectlyImpliedJudgements(context); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
71 | return TypeConstraintUtil.getFunctionalDependencies(context, supplierKey, variablesTuple); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
76 | super.doReplaceVariable(obsolete, replacement); | ||
77 | this.equivalentJudgement = new TypeJudgement(getSupplierKey(), variablesTuple); | ||
78 | } | ||
79 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java new file mode 100644 index 00000000..2c03a894 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java | |||
@@ -0,0 +1,231 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Optional; | ||
17 | import java.util.Set; | ||
18 | import java.util.stream.Collectors; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
24 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
28 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * Default implementation of PQuery. | ||
33 | * | ||
34 | * @author Bergmann Gabor | ||
35 | */ | ||
36 | public abstract class BasePQuery implements PQuery { | ||
37 | |||
38 | protected PQueryStatus status = PQueryStatus.UNINITIALIZED; | ||
39 | /** | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | protected final PVisibility visibility; | ||
43 | protected List<PProblem> pProblems = new ArrayList<PProblem>(); | ||
44 | private List<PAnnotation> annotations = new ArrayList<PAnnotation>(); | ||
45 | private QueryEvaluationHint evaluationHints = new QueryEvaluationHint(null, (IQueryBackendFactory)null); | ||
46 | PDisjunction canonicalDisjunction; | ||
47 | private List<String> parameterNames = null; // Lazy initialization | ||
48 | |||
49 | /** For traceability only. */ | ||
50 | private List<Object> wrappingQuerySpecifications = new ArrayList<Object>(1); | ||
51 | |||
52 | @Override | ||
53 | public Integer getPositionOfParameter(String parameterName) { | ||
54 | ensureInitialized(); | ||
55 | int index = getParameterNames().indexOf(parameterName); | ||
56 | return index != -1 ? index : null; | ||
57 | } | ||
58 | |||
59 | protected void setStatus(PQueryStatus newStatus) { | ||
60 | this.status = newStatus; | ||
61 | } | ||
62 | |||
63 | protected void addError(PProblem problem) { | ||
64 | status = PQueryStatus.ERROR; | ||
65 | pProblems.add(problem); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public PQueryStatus getStatus() { | ||
70 | return status; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public List<PProblem> getPProblems() { | ||
75 | return Collections.unmodifiableList(pProblems); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean isMutable() { | ||
80 | return status.equals(PQueryStatus.UNINITIALIZED) || status.equals(PQueryStatus.INITIALIZING); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void checkMutability() { | ||
85 | Preconditions.checkState(isMutable(), "Cannot edit query definition %s", getFullyQualifiedName()); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * @since 1.5 | ||
90 | */ | ||
91 | public void setEvaluationHints(QueryEvaluationHint hints) { | ||
92 | checkMutability(); | ||
93 | this.evaluationHints = hints; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public QueryEvaluationHint getEvaluationHints() { | ||
98 | ensureInitialized(); | ||
99 | return evaluationHints; | ||
100 | // TODO instead of field, compute something from annotations? | ||
101 | } | ||
102 | |||
103 | protected void addAnnotation(PAnnotation annotation) { | ||
104 | checkMutability(); | ||
105 | annotations.add(annotation); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public List<PAnnotation> getAllAnnotations() { | ||
110 | ensureInitialized(); | ||
111 | return new ArrayList<>(annotations); | ||
112 | } | ||
113 | |||
114 | private Stream<PAnnotation> getAnnotationStreamByName(final String name) { | ||
115 | ensureInitialized(); | ||
116 | return annotations.stream().filter(Objects::nonNull).filter(annotation -> Objects.equals(name, annotation.getName())); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public List<PAnnotation> getAnnotationsByName(final String annotationName) { | ||
121 | return getAnnotationStreamByName(annotationName).collect(Collectors.toList()); | ||
122 | } | ||
123 | |||
124 | @Override | ||
125 | public Optional<PAnnotation> getFirstAnnotationByName(String annotationName) { | ||
126 | return getAnnotationStreamByName(annotationName).findFirst(); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public List<String> getParameterNames() { | ||
131 | ensureInitialized(); | ||
132 | if (parameterNames == null) { | ||
133 | parameterNames = getParameters().stream().map(PParameter::getName).collect(Collectors.toList()); | ||
134 | } | ||
135 | return parameterNames; | ||
136 | } | ||
137 | |||
138 | @Override | ||
139 | public Set<PQuery> getDirectReferredQueries() { | ||
140 | ensureInitialized(); | ||
141 | return canonicalDisjunction.getDirectReferredQueries(); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public Set<PQuery> getAllReferredQueries() { | ||
146 | ensureInitialized(); | ||
147 | return canonicalDisjunction.getAllReferredQueries(); | ||
148 | } | ||
149 | |||
150 | |||
151 | @Override | ||
152 | public List<Object> publishedAs() { | ||
153 | return wrappingQuerySpecifications; | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public Set<TypeJudgement> getTypeGuarantees() { | ||
158 | ensureInitialized(); | ||
159 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
160 | |||
161 | List<PParameter> parameters = getParameters(); | ||
162 | for (int i=0; i<parameters.size(); ++i) { | ||
163 | PParameter parameter = parameters.get(i); | ||
164 | IInputKey declaredUnaryType = parameter.getDeclaredUnaryType(); | ||
165 | if (declaredUnaryType != null) { | ||
166 | result.add(new TypeJudgement(declaredUnaryType, Tuples.staticArityFlatTupleOf(i))); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | return result; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * @since 2.0 | ||
175 | */ | ||
176 | public BasePQuery(PVisibility visibility) { | ||
177 | super(); | ||
178 | this.visibility = visibility; | ||
179 | } | ||
180 | |||
181 | @Override | ||
182 | public PDisjunction getDisjunctBodies() { | ||
183 | ensureInitialized(); | ||
184 | Preconditions.checkState(!status.equals(PQueryStatus.ERROR), "Query %s contains errors.", getFullyQualifiedName()); | ||
185 | return canonicalDisjunction; | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | public final void ensureInitialized() { | ||
190 | try { | ||
191 | if (status.equals(PQueryStatus.UNINITIALIZED)) { | ||
192 | setStatus(PQueryStatus.INITIALIZING); | ||
193 | setBodies(doGetContainedBodies()); | ||
194 | setStatus(PQueryStatus.OK); | ||
195 | } | ||
196 | } catch (QueryInitializationException e) { | ||
197 | addError(new PProblem(e, e.getShortMessage())); | ||
198 | throw e; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | protected final void setBodies(Set<PBody> bodies) { | ||
203 | canonicalDisjunction = new PDisjunction(this, bodies); | ||
204 | for (PBody body : canonicalDisjunction.getBodies()) { | ||
205 | body.setStatus(null); | ||
206 | } | ||
207 | setStatus(PQueryStatus.OK); | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * Creates and returns the bodies of the query. If recalled again, a new instance is created. | ||
212 | * | ||
213 | * @return | ||
214 | * @throws ViatraQueryRuntimeException | ||
215 | */ | ||
216 | protected abstract Set<PBody> doGetContainedBodies(); | ||
217 | |||
218 | @Override | ||
219 | public String toString() { | ||
220 | return String.format("PQuery<%s>=%s", getFullyQualifiedName(), super.toString()); | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * @since 2.0 | ||
225 | */ | ||
226 | @Override | ||
227 | public PVisibility getVisibility() { | ||
228 | return visibility; | ||
229 | } | ||
230 | |||
231 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java new file mode 100644 index 00000000..eae4eacf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java | |||
@@ -0,0 +1,104 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.LinkedHashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | |||
18 | /** | ||
19 | * | ||
20 | * A disjunction is a set of bodies representing separate conditions. A {@link PQuery} has a single, canonical | ||
21 | * PDisjunction, that can be replaced using rewriter | ||
22 | * | ||
23 | * @author Zoltan Ujhelyi | ||
24 | * | ||
25 | */ | ||
26 | public class PDisjunction { | ||
27 | |||
28 | private Set<PBody> bodies; | ||
29 | private PQuery query; | ||
30 | |||
31 | public PDisjunction(Set<PBody> bodies) { | ||
32 | this(bodies.iterator().next().getPattern(), bodies); | ||
33 | } | ||
34 | |||
35 | public PDisjunction(PQuery query, Set<PBody> bodies) { | ||
36 | super(); | ||
37 | this.query = query; | ||
38 | this.bodies = Collections.unmodifiableSet(new LinkedHashSet<>(bodies)); | ||
39 | this.bodies.forEach(body -> body.setContainerDisjunction(this)); | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * Returns an immutable set of bodies that consists of this disjunction | ||
44 | * | ||
45 | * @return the bodies | ||
46 | */ | ||
47 | public Set<PBody> getBodies() { | ||
48 | return bodies; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Returns the corresponding query specification. May be null if not set. | ||
53 | */ | ||
54 | public PQuery getQuery() { | ||
55 | return query; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Returns all queries directly referred in the constraints. They are all required to evaluate this query | ||
60 | * | ||
61 | * @return a non-null, but possibly empty list of query definitions | ||
62 | */ | ||
63 | public Set<PQuery> getDirectReferredQueries() { | ||
64 | return this.getBodies().stream(). | ||
65 | flatMap(PQueries.directlyReferencedQueriesFunction()). // flatten stream of streams | ||
66 | collect(Collectors.toCollection(LinkedHashSet::new)); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Returns all queries required to evaluate this query (transitively). | ||
71 | * | ||
72 | * @return a non-null, but possibly empty list of query definitions | ||
73 | */ | ||
74 | public Set<PQuery> getAllReferredQueries() { | ||
75 | Set<PQuery> processedQueries = new LinkedHashSet<>(); | ||
76 | processedQueries.add(this.getQuery()); | ||
77 | Set<PQuery> foundQueries = getDirectReferredQueries(); | ||
78 | Set<PQuery> newQueries = new LinkedHashSet<>(foundQueries); | ||
79 | |||
80 | while(!processedQueries.containsAll(newQueries)) { | ||
81 | PQuery query = newQueries.iterator().next(); | ||
82 | processedQueries.add(query); | ||
83 | newQueries.remove(query); | ||
84 | Set<PQuery> referred = query.getDirectReferredQueries(); | ||
85 | referred.removeAll(processedQueries); | ||
86 | foundQueries.addAll(referred); | ||
87 | newQueries.addAll(referred); | ||
88 | } | ||
89 | return foundQueries; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Decides whether a disjunction is mutable. A disjunction is mutable if all its contained bodies are mutable. | ||
94 | * | ||
95 | */ | ||
96 | public boolean isMutable() { | ||
97 | for (PBody body : bodies) { | ||
98 | if (!body.isMutable()) { | ||
99 | return false; | ||
100 | } | ||
101 | } | ||
102 | return true; | ||
103 | } | ||
104 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java new file mode 100644 index 00000000..07165aa2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
14 | |||
15 | /** | ||
16 | * A descriptor for declared PQuery parameters. A parameter has a name, a declared type and a direction constraint | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * | ||
20 | */ | ||
21 | public class PParameter { | ||
22 | |||
23 | private final String name; | ||
24 | private final String typeName; | ||
25 | private final IInputKey declaredUnaryType; | ||
26 | private final PParameterDirection direction; | ||
27 | |||
28 | public PParameter(String name) { | ||
29 | this(name, (String) null); | ||
30 | } | ||
31 | |||
32 | public PParameter(String name, String typeName) { | ||
33 | this(name, typeName, (IInputKey) null); | ||
34 | } | ||
35 | |||
36 | public PParameter(String name, String typeName, IInputKey declaredUnaryType) { | ||
37 | this(name, typeName, declaredUnaryType, PParameterDirection.INOUT); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @since 1.4 | ||
42 | */ | ||
43 | public PParameter(String name, String typeName, IInputKey declaredUnaryType, PParameterDirection direction) { | ||
44 | super(); | ||
45 | this.name = name; | ||
46 | this.typeName = typeName; | ||
47 | this.declaredUnaryType = declaredUnaryType; | ||
48 | this.direction = direction; | ||
49 | |||
50 | if (declaredUnaryType != null && declaredUnaryType.getArity() != 1) { | ||
51 | throw new IllegalArgumentException( | ||
52 | "PParameter declared type must be unary instead of " + declaredUnaryType.getPrettyPrintableName()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * @return the direction | ||
58 | * @since 1.4 | ||
59 | */ | ||
60 | public PParameterDirection getDirection() { | ||
61 | return direction; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * @return the name of the parameter | ||
66 | */ | ||
67 | public String getName() { | ||
68 | return name; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Returns a textual representation of the declared type of the parameter | ||
73 | * | ||
74 | * @return the type description, or null if not available | ||
75 | */ | ||
76 | public String getTypeName() { | ||
77 | return typeName; | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * Yield an {@link IInputKey} representation of the type declared for this parameter. | ||
82 | * | ||
83 | * @return the unary type that was declared on this parameter in the query header, or null if not available | ||
84 | */ | ||
85 | public IInputKey getDeclaredUnaryType() { | ||
86 | return declaredUnaryType; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean equals(Object obj) { | ||
91 | if (obj instanceof PParameter) { | ||
92 | return Objects.equals(name, ((PParameter) obj).name) | ||
93 | && Objects.equals(typeName, ((PParameter) obj).typeName) | ||
94 | && Objects.equals(declaredUnaryType, ((PParameter) obj).declaredUnaryType) | ||
95 | && Objects.equals(direction, ((PParameter) obj).direction); | ||
96 | } | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public int hashCode() { | ||
102 | return Objects.hash(name, typeName, declaredUnaryType); | ||
103 | } | ||
104 | |||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java new file mode 100644 index 00000000..c94d4797 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.psystem.queries; | ||
10 | |||
11 | /** | ||
12 | * Values of this enum describe a constraint to the calling of patterns regarding its parameters. | ||
13 | * | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public enum PParameterDirection { | ||
19 | |||
20 | /** | ||
21 | * Default value, no additional constraint is applied | ||
22 | */ | ||
23 | INOUT, | ||
24 | |||
25 | /** | ||
26 | * The parameters marked with this constraints shall be set to a value before calling the pattern | ||
27 | */ | ||
28 | IN, | ||
29 | |||
30 | /** | ||
31 | * The parameters marked with this constraints shall not be set to a value before calling the pattern | ||
32 | */ | ||
33 | OUT | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java new file mode 100644 index 00000000..1fe4f541 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | |||
13 | /** | ||
14 | * Represents an error that was detected while the {@link PQuery} object was built from a source. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public class PProblem { | ||
19 | |||
20 | private final String shortMessage; | ||
21 | private final String location; | ||
22 | private final Exception exception; | ||
23 | |||
24 | public PProblem(String shortMessage) { | ||
25 | this(null, shortMessage, null, null); | ||
26 | } | ||
27 | /** | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | public PProblem(String shortMessage, Integer line, Integer column) { | ||
31 | this(null, shortMessage, line, column); | ||
32 | } | ||
33 | public PProblem(QueryProcessingException exception) { | ||
34 | this(exception, exception.getShortMessage(), null, null); | ||
35 | } | ||
36 | public PProblem(Exception exception, String shortMessage) { | ||
37 | this(exception, shortMessage, null, null); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @since 2.0 | ||
42 | */ | ||
43 | public PProblem(Exception exception, String shortMessage, Integer line, Integer column) { | ||
44 | this.shortMessage = shortMessage; | ||
45 | this.exception = exception; | ||
46 | if (line == null) { | ||
47 | location = "Unspecified location"; | ||
48 | } else if (column == null) { | ||
49 | location = String.format("Line %d", line); | ||
50 | } else { | ||
51 | location = String.format("Line %d Column %d", line, column); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | public String getShortMessage() { | ||
56 | return shortMessage; | ||
57 | } | ||
58 | public Exception getException() { | ||
59 | return exception; | ||
60 | } | ||
61 | /** | ||
62 | * @since 2.0 | ||
63 | */ | ||
64 | public String getLocation() { | ||
65 | return location; | ||
66 | } | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java new file mode 100644 index 00000000..56f8ca76 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java | |||
@@ -0,0 +1,110 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.Function; | ||
14 | import java.util.function.Predicate; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
24 | |||
25 | /** | ||
26 | * Utility class for using PQueries in functional/streaming collection operations effectively | ||
27 | * | ||
28 | * @author Zoltan Ujhelyi | ||
29 | * | ||
30 | */ | ||
31 | public final class PQueries { | ||
32 | |||
33 | /** | ||
34 | * Hidden constructor for utility class | ||
35 | */ | ||
36 | private PQueries() { | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Predicate checking for the status of selected queries | ||
41 | * | ||
42 | */ | ||
43 | public static Predicate<PQuery> queryStatusPredicate(final PQueryStatus status) { | ||
44 | return query -> query.getStatus().equals(status); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Enumerates referred queries (without duplicates) for the given body | ||
49 | */ | ||
50 | public static Function<PBody, Stream<PQuery>> directlyReferencedQueriesFunction() { | ||
51 | return body -> (body.getConstraintsOfType(IMultiQueryReference.class).stream() | ||
52 | .flatMap(e -> e.getReferredQueries().stream()).distinct()); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Enumerates directly referred extensional relations (without duplicates) in the canonical form of the given query | ||
57 | * | ||
58 | * @param enumerablesOnly | ||
59 | * only enumerable type constraints are considered | ||
60 | * @since 2.0 | ||
61 | */ | ||
62 | public static Stream<IInputKey> directlyRequiredTypesOfQuery(PQuery query, boolean enumerablesOnly) { | ||
63 | return directlyRequiredTypesOfDisjunction(query.getDisjunctBodies(), enumerablesOnly); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Enumerates directly referred extensional relations (without duplicates) for the given formulation of a query. | ||
68 | * | ||
69 | * @param enumerablesOnly | ||
70 | * only enumerable type constraints are considered | ||
71 | * @since 2.0 | ||
72 | */ | ||
73 | public static Stream<IInputKey> directlyRequiredTypesOfDisjunction(PDisjunction disjunctBodies, | ||
74 | boolean enumerablesOnly) { | ||
75 | Class<? extends ITypeConstraint> filterClass = enumerablesOnly ? TypeConstraint.class : ITypeConstraint.class; | ||
76 | return disjunctBodies.getBodies().stream().flatMap(body -> body.getConstraintsOfType(filterClass).stream()) | ||
77 | .map(constraint -> constraint.getEquivalentJudgement().getInputKey()).distinct(); | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * @since 1.4 | ||
82 | */ | ||
83 | public static Predicate<PParameter> parameterDirectionPredicate(final PParameterDirection direction) { | ||
84 | return input -> input.getDirection() == direction; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Returns all {@link PTraceable}s contained in the given {@link PQuery}: itself, its bodies and their constraints. | ||
89 | * | ||
90 | * @since 1.6 | ||
91 | */ | ||
92 | public static Set<PTraceable> getTraceables(PQuery query) { | ||
93 | final Set<PTraceable> traceables = new HashSet<>(); | ||
94 | traceables.add(query); | ||
95 | query.getDisjunctBodies().getBodies().forEach(body -> { | ||
96 | traceables.add(body); | ||
97 | body.getConstraints().forEach(traceables::add); | ||
98 | }); | ||
99 | return traceables; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Calculates the simple name related from a given qualified name by finding the part after the last '.' character. | ||
104 | * | ||
105 | * @since 2.0 | ||
106 | */ | ||
107 | public static String calculateSimpleName(String qualifiedName) { | ||
108 | return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); | ||
109 | } | ||
110 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java new file mode 100644 index 00000000..a909c650 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java | |||
@@ -0,0 +1,154 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
21 | |||
22 | /** | ||
23 | * Internal representation of a query / graph pattern (using a constraint system formalism), | ||
24 | * to be interpreted by a query evaluator ({@link IQueryBackend}). | ||
25 | * End-users of VIATRA Query should access a query as an IQuerySpecification instead. | ||
26 | * | ||
27 | * <p> | ||
28 | * PQuerys are definitions of queries usable inside pattern descriptions. Such description always has (a non-null) name. The query | ||
29 | * itself is defined as a (non-empty) set of {@link PBody} instances, the result is the disjunction of the single | ||
30 | * {@link PBody} instances. </p> | ||
31 | * <p> | ||
32 | * A PQuery might be constructed from erroneous patterns or might be uninitialized - this is represented by its status. | ||
33 | * | ||
34 | * @author Zoltan Ujhelyi | ||
35 | * @since 0.8.0 | ||
36 | * @noimplement This interface is not intended to be implemented by clients. Use {@link BasePQuery} as a base class instead. | ||
37 | */ | ||
38 | public interface PQuery extends PQueryHeader, PTraceable { | ||
39 | |||
40 | // TODO rewritten as / rewritten from traceability to PDisjunction? | ||
41 | |||
42 | /** | ||
43 | * @author Zoltan Ujhelyi | ||
44 | * | ||
45 | */ | ||
46 | public enum PQueryStatus { | ||
47 | /** | ||
48 | * Marks that the query definition is not initialized | ||
49 | */ | ||
50 | UNINITIALIZED, | ||
51 | /** | ||
52 | * Marks that the query definition is being initialized | ||
53 | * @since 1.4 | ||
54 | */ | ||
55 | INITIALIZING, | ||
56 | /** | ||
57 | * The query definition was successfully initialized | ||
58 | */ | ||
59 | OK, | ||
60 | /** | ||
61 | * The query definition was initialized, but some issues were present | ||
62 | */ | ||
63 | WARNING, | ||
64 | /** | ||
65 | * The query definition was not successfully initialized because of an error | ||
66 | */ | ||
67 | ERROR | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Returns all bodies associated with the query in their canonical form. If called multiple times, the same set with | ||
72 | * the same contents will be returned. | ||
73 | * | ||
74 | */ | ||
75 | PDisjunction getDisjunctBodies(); | ||
76 | |||
77 | /** | ||
78 | * Returns all queries directly referred in the constraints. They are all required to evaluate this query | ||
79 | * | ||
80 | * @return a non-null, but possibly empty list of query definitions | ||
81 | */ | ||
82 | Set<PQuery> getDirectReferredQueries(); | ||
83 | |||
84 | /** | ||
85 | * Returns all queries required to evaluate this query (transitively). | ||
86 | * | ||
87 | * @return a non-null, but possibly empty list of query definitions | ||
88 | */ | ||
89 | Set<PQuery> getAllReferredQueries(); | ||
90 | |||
91 | /** | ||
92 | * Returns the initialization status of the definition | ||
93 | * | ||
94 | */ | ||
95 | PQueryStatus getStatus(); | ||
96 | |||
97 | /** | ||
98 | * Returns a list describing the problems that were found in this query. | ||
99 | * | ||
100 | * <p> TODO: formulate invariant connecting {@link #getPProblems()} and {@link #getStatus()}. | ||
101 | * | ||
102 | * @return a non-null, but possibly empty list of problems | ||
103 | */ | ||
104 | List<PProblem> getPProblems(); | ||
105 | |||
106 | /** | ||
107 | * Before a modification operation is executed, a mutability check is performed (via the {@link #getStatus()} | ||
108 | * implementation, and in case of problems an {@link IllegalStateException} is thrown. | ||
109 | */ | ||
110 | void checkMutability(); | ||
111 | |||
112 | /** | ||
113 | * An option to check mutability of the query. It can be used to avoid getting an {@link IllegalStateException} by | ||
114 | * the execution of {@link #checkMutability()}. | ||
115 | * | ||
116 | * @return true if the query specification is still editable | ||
117 | */ | ||
118 | boolean isMutable(); | ||
119 | |||
120 | /** | ||
121 | * Optional hints regarding the query evaluation strategy, to be interpreted by the query engine. | ||
122 | * <p> To ensure the possibility of external overrides, | ||
123 | * the evaluation engine should not directly consult this field, | ||
124 | * but use an {@link IQueryBackendHintProvider} instead. | ||
125 | */ | ||
126 | public QueryEvaluationHint getEvaluationHints(); | ||
127 | |||
128 | |||
129 | /** | ||
130 | * Type information, expressed on query parameters, that all matches of the query are guaranteed to respect. | ||
131 | * <p> At the very minimum, this should include the declared types of the parameters. | ||
132 | * <p> The type judgement tuples shall contain the <i>parameter index</i>, NOT the {@link PParameter} object. | ||
133 | * | ||
134 | * @return a non-null set of type judgements that the query guarantees for its matches | ||
135 | */ | ||
136 | public Set<TypeJudgement> getTypeGuarantees(); | ||
137 | |||
138 | /** | ||
139 | * If the query definition is uninitialized, initializes it. | ||
140 | * @throws ViatraQueryRuntimeException if initialization of query specification fails | ||
141 | */ | ||
142 | public abstract void ensureInitialized(); | ||
143 | |||
144 | /** | ||
145 | * Returns the end-user query specification API objects that wrap this query. | ||
146 | * | ||
147 | * <p> Intended for traceability and debug purposes, not part of normal operation. | ||
148 | * Returned list is intended to be appended during query specification construction time. | ||
149 | * | ||
150 | * @return a non-null, but possibly empty list of query specification objects; | ||
151 | */ | ||
152 | List<Object> publishedAs(); | ||
153 | |||
154 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java new file mode 100644 index 00000000..f3671934 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java | |||
@@ -0,0 +1,101 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
15 | |||
16 | /** | ||
17 | * Represents header information (metainfo) about a query. | ||
18 | * <p> To be implemented both by IQuerySpecifications intended for end users, | ||
19 | * and the internal query representation {@link PQuery}. | ||
20 | * | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * @since 0.9 | ||
24 | */ | ||
25 | public interface PQueryHeader { | ||
26 | |||
27 | /** | ||
28 | * Identifies the pattern for which matchers can be instantiated. | ||
29 | */ | ||
30 | public String getFullyQualifiedName(); | ||
31 | |||
32 | /** | ||
33 | * Return the list of parameter names | ||
34 | * | ||
35 | * @return a non-null, but possibly empty list of parameter names | ||
36 | */ | ||
37 | public List<String> getParameterNames(); | ||
38 | |||
39 | /** | ||
40 | * Returns a list of parameter descriptions | ||
41 | * | ||
42 | * @return a non-null, but possibly empty list of parameter descriptions | ||
43 | */ | ||
44 | public List<PParameter> getParameters(); | ||
45 | |||
46 | /** | ||
47 | * Returns the index of a named parameter | ||
48 | * | ||
49 | * @param parameterName | ||
50 | * @return the index, or null of no such parameter is available | ||
51 | */ | ||
52 | public Integer getPositionOfParameter(String parameterName); | ||
53 | |||
54 | /** | ||
55 | * Returns a parameter by name if exists | ||
56 | * @since 2.1 | ||
57 | */ | ||
58 | default Optional<PParameter> getParameter(String parameterName) { | ||
59 | return Optional.ofNullable(getPositionOfParameter(parameterName)) | ||
60 | .map(getParameters()::get); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Returns the list of annotations specified for this query | ||
65 | * | ||
66 | * @return a non-null, but possibly empty list of annotations | ||
67 | */ | ||
68 | public List<PAnnotation> getAllAnnotations(); | ||
69 | |||
70 | /** | ||
71 | * Returns the list of annotations with a specified name | ||
72 | * | ||
73 | * @param annotationName | ||
74 | * @return a non-null, but possibly empty list of annotations | ||
75 | */ | ||
76 | public List<PAnnotation> getAnnotationsByName(String annotationName); | ||
77 | |||
78 | /** | ||
79 | * Returns the first annotation with a specified name | ||
80 | * | ||
81 | * @since 2.0 | ||
82 | */ | ||
83 | public Optional<PAnnotation> getFirstAnnotationByName(String annotationName); | ||
84 | |||
85 | /** | ||
86 | * Returns the visibility information about the query. | ||
87 | * @since 2.0 | ||
88 | */ | ||
89 | public PVisibility getVisibility(); | ||
90 | |||
91 | /** | ||
92 | * Returns the non-qualified name of the query. By default this means returning the qualified name after the last | ||
93 | * '.' character. | ||
94 | * | ||
95 | * @since 2.0 | ||
96 | */ | ||
97 | public default String getSimpleName() { | ||
98 | return PQueries.calculateSimpleName(getFullyQualifiedName()); | ||
99 | } | ||
100 | |||
101 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java new file mode 100644 index 00000000..7cb312bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.psystem.queries; | ||
10 | |||
11 | /** | ||
12 | * @author Zoltan Ujhelyi | ||
13 | * @since 2.0 | ||
14 | * | ||
15 | */ | ||
16 | public enum PVisibility { | ||
17 | |||
18 | /** | ||
19 | * A public (default) visibility means a pattern can be called at any time. | ||
20 | */ | ||
21 | PUBLIC, | ||
22 | /** | ||
23 | * A private query is not expected to be called directly, only by a different query matcher. | ||
24 | */ | ||
25 | PRIVATE, | ||
26 | /** | ||
27 | * A query that is only used inside a single caller query and is not visible outside its container query. Such | ||
28 | * patterns must also fulfill the following additional constraints: | ||
29 | * | ||
30 | * <ul> | ||
31 | * <li>An embedded query must have only a single body.</li> | ||
32 | * <li>An embedded query must not be recursice.</li> | ||
33 | * </ul> | ||
34 | */ | ||
35 | EMBEDDED | ||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java new file mode 100644 index 00000000..470d7287 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.psystem.queries; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | |||
13 | /** | ||
14 | * Represent an exception that occurred while initializing the specification of a query. | ||
15 | * @author Bergmann Gabor | ||
16 | * @since 0.9 | ||
17 | * | ||
18 | */ | ||
19 | public class QueryInitializationException extends QueryProcessingException { | ||
20 | |||
21 | public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription, | ||
22 | Throwable cause) { | ||
23 | super(message, context, shortMessage, patternDescription, cause); | ||
24 | } | ||
25 | |||
26 | public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
27 | super(message, context, shortMessage, patternDescription); | ||
28 | } | ||
29 | |||
30 | private static final long serialVersionUID = 9106033062252951489L; | ||
31 | |||
32 | |||
33 | |||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java new file mode 100644 index 00000000..276b2b42 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
15 | |||
16 | /** | ||
17 | * @since 1.6 | ||
18 | * | ||
19 | */ | ||
20 | public class AbstractRewriterTraceSource { | ||
21 | |||
22 | private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE; | ||
23 | |||
24 | public void setTraceCollector(IRewriterTraceCollector traceCollector) { | ||
25 | this.traceCollector = Objects.requireNonNull(traceCollector); | ||
26 | } | ||
27 | |||
28 | public IPTraceableTraceProvider getTraces() { | ||
29 | return traceCollector; | ||
30 | } | ||
31 | |||
32 | protected IRewriterTraceCollector getTraceCollector() { | ||
33 | return traceCollector; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Mark the given derivative to be originated from the given original constraint. | ||
38 | * @since 1.6 | ||
39 | */ | ||
40 | protected void addTrace(PTraceable original, PTraceable derivative){ | ||
41 | traceCollector.addTrace(original, derivative); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Indicate that the given derivative is removed from the resulting query, thus its trace | ||
46 | * information should be removed also. | ||
47 | * @since 1.6 | ||
48 | */ | ||
49 | protected void derivativeRemoved(PConstraint derivative, IDerivativeModificationReason reason){ | ||
50 | traceCollector.derivativeRemoved(derivative, reason); | ||
51 | } | ||
52 | |||
53 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java new file mode 100644 index 00000000..237a762d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | /** | ||
12 | * Common reasons for removing constraint through rewriters | ||
13 | * | ||
14 | * @noreference This enum is not intended to be referenced by clients. | ||
15 | */ | ||
16 | public enum ConstraintRemovalReason implements IDerivativeModificationReason { | ||
17 | |||
18 | MOOT_EQUALITY, | ||
19 | WEAK_INEQUALITY_SELF_LOOP, | ||
20 | TYPE_SUBSUMED, | ||
21 | DUPLICATE | ||
22 | |||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java new file mode 100644 index 00000000..3b5d7390 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
11 | |||
12 | /** | ||
13 | * @author Marton Bur | ||
14 | * | ||
15 | */ | ||
16 | public class DefaultFlattenCallPredicate implements IFlattenCallPredicate { | ||
17 | |||
18 | @Override | ||
19 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
20 | return true; | ||
21 | } | ||
22 | |||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java new file mode 100644 index 00000000..06b8d372 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java | |||
@@ -0,0 +1,129 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
28 | |||
29 | /** | ||
30 | * This rewriter class can add new equality constraints to the copied body | ||
31 | * | ||
32 | * @author Marton Bur | ||
33 | * | ||
34 | */ | ||
35 | class FlattenerCopier extends PBodyCopier { | ||
36 | |||
37 | private final Map<PositivePatternCall, CallInformation> calls; | ||
38 | |||
39 | private static class CallInformation { | ||
40 | final PBody body; | ||
41 | final Map<PVariable, PVariable> variableMapping; | ||
42 | |||
43 | private CallInformation(PBody body) { | ||
44 | this.body = body; | ||
45 | this.variableMapping = new HashMap<>(); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | public FlattenerCopier(PQuery query, Map<PositivePatternCall, PBody> callsToFlatten) { | ||
50 | super(query); | ||
51 | this.calls = callsToFlatten.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> new CallInformation(entry.getValue()))); | ||
52 | } | ||
53 | |||
54 | protected void copyVariable(PositivePatternCall contextPatternCall, PVariable variable, String newName) { | ||
55 | PVariable newPVariable = body.getOrCreateVariableByName(newName); | ||
56 | calls.get(contextPatternCall).variableMapping.put(variable, newPVariable); | ||
57 | variableMapping.put(variable, newPVariable); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Merge all variables and constraints from the body called through the given pattern call to a target body. If | ||
62 | * multiple bodies are merged into a single one, use the renamer and filter options to avoid collisions. | ||
63 | * | ||
64 | * @param sourceBody | ||
65 | * @param namingTool | ||
66 | * @param filter | ||
67 | */ | ||
68 | public void mergeBody(PositivePatternCall contextPatternCall, IVariableRenamer namingTool, | ||
69 | IConstraintFilter filter) { | ||
70 | |||
71 | PBody sourceBody = calls.get(contextPatternCall).body; | ||
72 | |||
73 | // Copy variables | ||
74 | Set<PVariable> allVariables = sourceBody.getAllVariables(); | ||
75 | for (PVariable pVariable : allVariables) { | ||
76 | if (pVariable.isUnique()) { | ||
77 | copyVariable(contextPatternCall, pVariable, | ||
78 | namingTool.createVariableName(pVariable, sourceBody.getPattern())); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // Copy constraints which are not filtered | ||
83 | Set<PConstraint> constraints = sourceBody.getConstraints(); | ||
84 | for (PConstraint pConstraint : constraints) { | ||
85 | if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { | ||
86 | copyConstraint(pConstraint); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { | ||
93 | |||
94 | if (!calls.containsKey(positivePatternCall)) { | ||
95 | // If the call was not flattened, copy the constraint | ||
96 | super.copyPositivePatternCallConstraint(positivePatternCall); | ||
97 | } else { | ||
98 | PBody calledBody = Objects.requireNonNull(calls.get(positivePatternCall).body); | ||
99 | Preconditions.checkArgument(positivePatternCall.getReferredQuery().equals(calledBody.getPattern())); | ||
100 | |||
101 | List<PVariable> symbolicParameters = calledBody.getSymbolicParameterVariables(); | ||
102 | Object[] elements = positivePatternCall.getVariablesTuple().getElements(); | ||
103 | for (int i = 0; i < elements.length; i++) { | ||
104 | // Create equality constraints between the caller PositivePatternCall and the corresponding body | ||
105 | // parameter variables | ||
106 | createEqualityConstraint((PVariable) elements[i], symbolicParameters.get(i), positivePatternCall); | ||
107 | } | ||
108 | |||
109 | } | ||
110 | } | ||
111 | |||
112 | private void createEqualityConstraint(PVariable pVariable1, PVariable pVariable2, | ||
113 | PositivePatternCall contextPatternCall) { | ||
114 | PVariable who = variableMapping.get(pVariable1); | ||
115 | PVariable withWhom = calls.get(contextPatternCall).variableMapping.get(pVariable2); | ||
116 | addTrace(contextPatternCall, new Equality(body, who, withWhom)); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | protected void copyExpressionEvaluationConstraint(final ExpressionEvaluation expressionEvaluation) { | ||
121 | Map<PVariable, PVariable> variableMapping = this.variableMapping.entrySet().stream() | ||
122 | .filter(input -> expressionEvaluation.getPSystem().getAllVariables().contains(input.getKey())) | ||
123 | .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); | ||
124 | |||
125 | PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); | ||
126 | addTrace(expressionEvaluation, new ExpressionEvaluation(body, new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), mappedOutputVariable, expressionEvaluation.isUnwinding())); | ||
127 | } | ||
128 | |||
129 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java new file mode 100644 index 00000000..518b9c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
13 | |||
14 | /** | ||
15 | * Helper interface to exclude constraints from PBody copy processes | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IConstraintFilter { | ||
21 | /** | ||
22 | * Returns true, if the given constraint should be filtered (thus should not be copied) | ||
23 | * | ||
24 | * @param constraint | ||
25 | * to check | ||
26 | * @return true, if the constraint should be filtered | ||
27 | */ | ||
28 | boolean filter(PConstraint constraint); | ||
29 | |||
30 | public static class ExportedParameterFilter implements IConstraintFilter { | ||
31 | |||
32 | @Override | ||
33 | public boolean filter(PConstraint constraint) { | ||
34 | return constraint instanceof ExportedParameter; | ||
35 | } | ||
36 | |||
37 | } | ||
38 | |||
39 | public static class AllowAllFilter implements IConstraintFilter { | ||
40 | |||
41 | @Override | ||
42 | public boolean filter(PConstraint constraint) { | ||
43 | // Nothing is filtered | ||
44 | return false; | ||
45 | } | ||
46 | |||
47 | } | ||
48 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java new file mode 100644 index 00000000..dbd6a78d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | /** | ||
12 | * This is a role indication interface, implementations may provide a reason about | ||
13 | * why a modification is made during PQuery normalization. | ||
14 | * @since 1.6 | ||
15 | * | ||
16 | */ | ||
17 | public interface IDerivativeModificationReason { | ||
18 | |||
19 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java new file mode 100644 index 00000000..7e224e98 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | |||
13 | |||
14 | /** | ||
15 | * Interface used by the PQueryFlattener to decide which positive pattern calls to flatten | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IFlattenCallPredicate { | ||
21 | |||
22 | /** | ||
23 | * Decides whether the called query by the pattern call should be flattened into the caller or not. | ||
24 | * | ||
25 | * @param positivePatternCall | ||
26 | * the pattern call | ||
27 | * @return true if the call should be flattened | ||
28 | */ | ||
29 | boolean shouldFlatten(PositivePatternCall positivePatternCall); | ||
30 | |||
31 | /** | ||
32 | * Flattens only if all operand predicates vote for flattening. | ||
33 | * @author Gabor Bergmann | ||
34 | * @since 2.1 | ||
35 | */ | ||
36 | public static class And implements IFlattenCallPredicate { | ||
37 | private IFlattenCallPredicate[] operands; | ||
38 | public And(IFlattenCallPredicate... operands) { | ||
39 | this.operands = operands; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
44 | for (IFlattenCallPredicate operand : operands) { | ||
45 | if (!operand.shouldFlatten(positivePatternCall)) return false; | ||
46 | } | ||
47 | return true; | ||
48 | } | ||
49 | } | ||
50 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java new file mode 100644 index 00000000..84da4d1b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * This interface provides methods to trace the {@link PTraceable}s of a transformed {@link PQuery} produced by | ||
18 | * a {@link PDisjunctionRewriter}. In case the associated rewriter is a composite (a.k.a. {@link PDisjunctionRewriterCacher}), | ||
19 | * this trace provider handles traces end-to-end, hiding all the intermediate transformation steps. | ||
20 | * | ||
21 | * @since 1.6 | ||
22 | * @noimplement This interface is not intended to be implemented by clients. | ||
23 | */ | ||
24 | public interface IPTraceableTraceProvider { | ||
25 | |||
26 | /** | ||
27 | * Find and return the canonical {@link PTraceable}s in the original query which are the sources of the given derivative | ||
28 | * {@link PTraceable} according to the transformation. | ||
29 | * | ||
30 | * @param derivative a {@link PTraceable} which is contained by the {@link PQuery} produced by the associated rewriter | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative); | ||
34 | |||
35 | /** | ||
36 | * Find and return the {@link PTraceable}s in the rewritten query which are the destinations of the given source | ||
37 | * {@link PTraceable} according to the transformation. | ||
38 | * | ||
39 | * @param source a {@link PTraceable} which is contained by a {@link PQuery} before rewriting | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source); | ||
43 | |||
44 | /** | ||
45 | * Returns whether the given traceable element has been removed by every rewriter for a reason. | ||
46 | */ | ||
47 | public boolean isRemoved(PTraceable traceable); | ||
48 | |||
49 | /** | ||
50 | * Returns the reasons for which the traceable element has been removed by the rewriters. | ||
51 | * @return the reasons of removal during rewriting | ||
52 | * @since 2.0 | ||
53 | */ | ||
54 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable); | ||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java new file mode 100644 index 00000000..70771ea7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
12 | |||
13 | /** | ||
14 | * This is the internal API of {@link IPTraceableTraceProvider} expected to be used by | ||
15 | * copier and rewriter implementations. | ||
16 | * | ||
17 | * @since 1.6 | ||
18 | * @noreference This interface is not intended to be referenced by clients. | ||
19 | */ | ||
20 | public interface IRewriterTraceCollector extends IPTraceableTraceProvider { | ||
21 | |||
22 | /** | ||
23 | * Mark the given derivative to be originated from the given original constraint. | ||
24 | */ | ||
25 | public void addTrace(PTraceable origin, PTraceable derivative); | ||
26 | |||
27 | /** | ||
28 | * Indicate that the given derivative is removed from the resulting query, thus its trace | ||
29 | * information should be removed also. | ||
30 | */ | ||
31 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason); | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java new file mode 100644 index 00000000..ce446e0d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * Helper interface to ease the naming of the new variables during flattening | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IVariableRenamer { | ||
21 | /** | ||
22 | * Creates a variable name based on a given variable and a given query. It only creates a String, doesn't set | ||
23 | * anything. | ||
24 | * | ||
25 | * @param pVariable | ||
26 | * @param query | ||
27 | * @return the new variable name as a String | ||
28 | */ | ||
29 | String createVariableName(PVariable pVariable, PQuery query); | ||
30 | |||
31 | public class SameName implements IVariableRenamer { | ||
32 | @Override | ||
33 | public String createVariableName(PVariable pVariable, PQuery query) { | ||
34 | return pVariable.getName(); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public class HierarchicalName implements IVariableRenamer { | ||
39 | |||
40 | private int callCount; | ||
41 | |||
42 | public void setCallCount(int callCount) { | ||
43 | this.callCount = callCount; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public String createVariableName(PVariable pVariable, PQuery query) { | ||
48 | // make sure to keep the "_" prefix before anonymous variables | ||
49 | String newVarName = getShortName(query) + "<" + callCount + ">" + "_" + pVariable.getName(); | ||
50 | return pVariable.getName().startsWith("_") ? "_" + newVarName : newVarName ; | ||
51 | } | ||
52 | |||
53 | private String getShortName(PQuery query) { | ||
54 | String fullyQualifiedName = query.getFullyQualifiedName(); | ||
55 | int beginIndex = fullyQualifiedName.lastIndexOf('.') + 1; | ||
56 | return fullyQualifiedName.substring(beginIndex); | ||
57 | } | ||
58 | } | ||
59 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java new file mode 100644 index 00000000..7429fc60 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java | |||
@@ -0,0 +1,135 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.Map; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Predicate; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
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.Preconditions; | ||
28 | |||
29 | /** | ||
30 | * Multimap-based implementation to contain and query traces | ||
31 | * | ||
32 | * @since 1.6 | ||
33 | * | ||
34 | */ | ||
35 | public class MappingTraceCollector implements IRewriterTraceCollector { | ||
36 | |||
37 | /** | ||
38 | * Traces from derivative to original | ||
39 | */ | ||
40 | private final IMultiLookup<PTraceable, PTraceable> traces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
41 | |||
42 | /** | ||
43 | * Traces from original to derivative | ||
44 | */ | ||
45 | private final IMultiLookup<PTraceable, PTraceable> inverseTraces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
46 | |||
47 | /** | ||
48 | * Reasons for removing {@link PTraceable}s | ||
49 | */ | ||
50 | private final Map<PTraceable, IDerivativeModificationReason> removals = new HashMap<>(); | ||
51 | |||
52 | /** | ||
53 | * Decides whether {@link PTraceable} is removed | ||
54 | */ | ||
55 | private final Predicate<PTraceable> removed = removals::containsKey; | ||
56 | |||
57 | /** | ||
58 | * @since 2.0 | ||
59 | */ | ||
60 | @Override | ||
61 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) { | ||
62 | return findTraceEnds(derivative, traces).stream(); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | @Override | ||
69 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source) { | ||
70 | return findTraceEnds(source, inverseTraces).stream(); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Returns the end of trace chains starting from the given {@link PTraceable} along the given trace edges. | ||
75 | */ | ||
76 | private Set<PTraceable> findTraceEnds(PTraceable traceable, IMultiLookup<PTraceable, PTraceable> traceRecords) { | ||
77 | if (traceable instanceof PQuery) { // PQueries are preserved | ||
78 | return Collections.singleton(traceable); | ||
79 | } | ||
80 | Set<PTraceable> visited = new HashSet<>(); | ||
81 | Set<PTraceable> result = new HashSet<>(); | ||
82 | Queue<PTraceable> queue = new LinkedList<>(); | ||
83 | queue.add(traceable); | ||
84 | while(!queue.isEmpty()){ | ||
85 | PTraceable aDerivative = queue.poll(); | ||
86 | // Track visited elements to avoid infinite loop via directed cycles in traces | ||
87 | visited.add(aDerivative); | ||
88 | IMemoryView<PTraceable> nextOrigins = traceRecords.lookup(aDerivative); | ||
89 | if (nextOrigins == null){ | ||
90 | // End of trace chain | ||
91 | result.add(aDerivative); | ||
92 | } else { | ||
93 | // Follow traces | ||
94 | for(PTraceable nextOrigin : nextOrigins){ | ||
95 | if (!visited.contains(nextOrigin)){ | ||
96 | queue.add(nextOrigin); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | return result; | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void addTrace(PTraceable original, PTraceable derivative){ | ||
106 | traces.addPairOrNop(derivative, original); | ||
107 | inverseTraces.addPairOrNop(original, derivative); | ||
108 | // Even if this element was marked as removed earlier, now we replace it with another constraint! | ||
109 | removals.remove(original); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason){ | ||
114 | Preconditions.checkState(!removals.containsKey(derivative), "Traceable %s removed multiple times", derivative); | ||
115 | // XXX the derivative must not be removed from the trace chain, as some rewriters, e.g. the normalizer keeps trace links to deleted elements | ||
116 | if (!inverseTraces.lookupExists(derivative)) { | ||
117 | // If there already exists a trace link, this removal means an update | ||
118 | removals.put(derivative, reason); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public boolean isRemoved(PTraceable traceable) { | ||
124 | return getRewrittenTraceables(traceable).allMatch(removed); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * @since 2.0 | ||
129 | */ | ||
130 | @Override | ||
131 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) { | ||
132 | return getRewrittenTraceables(traceable).filter(removed).map(removals::get); | ||
133 | } | ||
134 | |||
135 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java new file mode 100644 index 00000000..96c0b205 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | |||
13 | /** | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public class NeverFlattenCallPredicate implements IFlattenCallPredicate { | ||
19 | |||
20 | |||
21 | @Override | ||
22 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
23 | return false; | ||
24 | } | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java new file mode 100644 index 00000000..15cf577e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
14 | |||
15 | /** | ||
16 | * This implementation does not store any traces and scales to NOP for every traceability feature. | ||
17 | * @since 1.6 | ||
18 | * | ||
19 | */ | ||
20 | public class NopTraceCollector implements IRewriterTraceCollector { | ||
21 | |||
22 | public static final IRewriterTraceCollector INSTANCE = new NopTraceCollector(); | ||
23 | |||
24 | private NopTraceCollector() { | ||
25 | // Private constructor to force using the common instance | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * @since 2.0 | ||
30 | */ | ||
31 | @Override | ||
32 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) { | ||
33 | return Stream.empty(); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 2.0 | ||
38 | */ | ||
39 | @Override | ||
40 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source) { | ||
41 | return Stream.empty(); | ||
42 | } | ||
43 | |||
44 | |||
45 | @Override | ||
46 | public void addTrace(PTraceable origin, PTraceable derivative) { | ||
47 | // ignored | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason) { | ||
52 | // ignored | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean isRemoved(PTraceable traceable) { | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * @since 2.0 | ||
62 | */ | ||
63 | @Override | ||
64 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) { | ||
65 | return Stream.empty(); | ||
66 | } | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java new file mode 100644 index 00000000..10ab19c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java | |||
@@ -0,0 +1,307 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | import java.util.*; | ||
27 | import java.util.stream.Collectors; | ||
28 | |||
29 | /** | ||
30 | * This class can create a new PBody for a PQuery. The result body contains a copy of given variables and constraints. | ||
31 | * | ||
32 | * @author Marton Bur | ||
33 | * | ||
34 | */ | ||
35 | public class PBodyCopier extends AbstractRewriterTraceSource { | ||
36 | |||
37 | /** | ||
38 | * The created body | ||
39 | */ | ||
40 | protected PBody body; | ||
41 | /** | ||
42 | * Mapping between the original and the copied variables | ||
43 | */ | ||
44 | protected Map<PVariable, PVariable> variableMapping = new HashMap<>(); | ||
45 | |||
46 | public Map<PVariable, PVariable> getVariableMapping() { | ||
47 | return variableMapping; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * @since 1.6 | ||
52 | */ | ||
53 | public PBodyCopier(PBody body, IRewriterTraceCollector traceCollector) { | ||
54 | this.body = new PBody(body.getPattern()); | ||
55 | setTraceCollector(traceCollector); | ||
56 | |||
57 | // do the actual copying | ||
58 | mergeBody(body); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * @since 1.6 | ||
63 | */ | ||
64 | public PBodyCopier(PQuery query) { | ||
65 | this.body = new PBody(query); | ||
66 | } | ||
67 | |||
68 | public void mergeBody(PBody sourceBody) { | ||
69 | mergeBody(sourceBody, new SameName(), new AllowAllFilter()); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Merge all variables and constraints from a source body to a target body. If multiple bodies are merged into a | ||
74 | * single one, use the renamer and filter options to avoid collisions. | ||
75 | */ | ||
76 | public void mergeBody(PBody sourceBody, IVariableRenamer namingTool, IConstraintFilter filter) { | ||
77 | |||
78 | // Copy variables | ||
79 | Set<PVariable> allVariables = sourceBody.getAllVariables(); | ||
80 | for (PVariable pVariable : allVariables) { | ||
81 | if (pVariable.isUnique()) { | ||
82 | copyVariable(pVariable, namingTool.createVariableName(pVariable, sourceBody.getPattern())); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | // Copy exported parameters | ||
87 | this.body.setSymbolicParameters(sourceBody.getSymbolicParameters().stream() | ||
88 | .map(this::copyExportedParameterConstraint).collect(Collectors.toList())); | ||
89 | |||
90 | // Copy constraints which are not filtered | ||
91 | Set<PConstraint> constraints = sourceBody.getConstraints(); | ||
92 | for (PConstraint pConstraint : constraints) { | ||
93 | if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { | ||
94 | copyConstraint(pConstraint); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | // Add trace between original and copied body | ||
99 | addTrace(sourceBody, body); | ||
100 | } | ||
101 | |||
102 | protected void copyVariable(PVariable variable, String newName) { | ||
103 | PVariable newPVariable = body.getOrCreateVariableByName(newName); | ||
104 | variableMapping.put(variable, newPVariable); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * Returns the body with the copied variables and constraints. The returned body is still uninitialized. | ||
109 | */ | ||
110 | public PBody getCopiedBody() { | ||
111 | return body; | ||
112 | } | ||
113 | |||
114 | protected void copyConstraint(PConstraint constraint) { | ||
115 | if (constraint instanceof ExportedParameter) { | ||
116 | copyExportedParameterConstraint((ExportedParameter) constraint); | ||
117 | } else if (constraint instanceof Equality) { | ||
118 | copyEqualityConstraint((Equality) constraint); | ||
119 | } else if (constraint instanceof Inequality) { | ||
120 | copyInequalityConstraint((Inequality) constraint); | ||
121 | } else if (constraint instanceof TypeConstraint) { | ||
122 | copyTypeConstraint((TypeConstraint) constraint); | ||
123 | } else if (constraint instanceof TypeFilterConstraint) { | ||
124 | copyTypeFilterConstraint((TypeFilterConstraint) constraint); | ||
125 | } else if (constraint instanceof ConstantValue) { | ||
126 | copyConstantValueConstraint((ConstantValue) constraint); | ||
127 | } else if (constraint instanceof PositivePatternCall) { | ||
128 | copyPositivePatternCallConstraint((PositivePatternCall) constraint); | ||
129 | } else if (constraint instanceof NegativePatternCall) { | ||
130 | copyNegativePatternCallConstraint((NegativePatternCall) constraint); | ||
131 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
132 | copyBinaryTransitiveClosureConstraint((BinaryTransitiveClosure) constraint); | ||
133 | } else if (constraint instanceof RepresentativeElectionConstraint) { | ||
134 | copyRepresentativeElectionConstraint((RepresentativeElectionConstraint) constraint); | ||
135 | } else if (constraint instanceof RelationEvaluation) { | ||
136 | copyRelationEvaluationConstraint((RelationEvaluation) constraint); | ||
137 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | ||
138 | copyBinaryReflexiveTransitiveClosureConstraint((BinaryReflexiveTransitiveClosure) constraint); | ||
139 | } else if (constraint instanceof PatternMatchCounter) { | ||
140 | copyPatternMatchCounterConstraint((PatternMatchCounter) constraint); | ||
141 | } else if (constraint instanceof AggregatorConstraint) { | ||
142 | copyAggregatorConstraint((AggregatorConstraint) constraint); | ||
143 | } else if (constraint instanceof ExpressionEvaluation) { | ||
144 | copyExpressionEvaluationConstraint((ExpressionEvaluation) constraint); | ||
145 | } else { | ||
146 | throw new QueryProcessingException("Unknown PConstraint {0} encountered while copying PBody", | ||
147 | new String[] { constraint.getClass().getName() }, "Unknown PConstraint", body.getPattern()); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | protected ExportedParameter copyExportedParameterConstraint(ExportedParameter exportedParameter) { | ||
152 | PVariable mappedPVariable = variableMapping.get(exportedParameter.getParameterVariable()); | ||
153 | PParameter parameter = exportedParameter.getPatternParameter(); | ||
154 | ExportedParameter newExportedParameter; | ||
155 | newExportedParameter = new ExportedParameter(body, mappedPVariable, parameter); | ||
156 | body.getSymbolicParameters().add(newExportedParameter); | ||
157 | addTrace(exportedParameter, newExportedParameter); | ||
158 | return newExportedParameter; | ||
159 | } | ||
160 | |||
161 | protected void copyEqualityConstraint(Equality equality) { | ||
162 | PVariable who = equality.getWho(); | ||
163 | PVariable withWhom = equality.getWithWhom(); | ||
164 | addTrace(equality, new Equality(body, variableMapping.get(who), variableMapping.get(withWhom))); | ||
165 | } | ||
166 | |||
167 | protected void copyInequalityConstraint(Inequality inequality) { | ||
168 | PVariable who = inequality.getWho(); | ||
169 | PVariable withWhom = inequality.getWithWhom(); | ||
170 | addTrace(inequality, new Inequality(body, variableMapping.get(who), variableMapping.get(withWhom))); | ||
171 | } | ||
172 | |||
173 | protected void copyTypeConstraint(TypeConstraint typeConstraint) { | ||
174 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
175 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
176 | addTrace(typeConstraint, new TypeConstraint(body, variablesTuple, typeConstraint.getSupplierKey())); | ||
177 | } | ||
178 | |||
179 | protected void copyTypeFilterConstraint(TypeFilterConstraint typeConstraint) { | ||
180 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
181 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
182 | addTrace(typeConstraint, new TypeFilterConstraint(body, variablesTuple, typeConstraint.getInputKey())); | ||
183 | } | ||
184 | |||
185 | protected void copyConstantValueConstraint(ConstantValue constantValue) { | ||
186 | PVariable pVariable = (PVariable) constantValue.getVariablesTuple().getElements()[0]; | ||
187 | addTrace(constantValue, | ||
188 | new ConstantValue(body, variableMapping.get(pVariable), constantValue.getSupplierKey())); | ||
189 | } | ||
190 | |||
191 | protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { | ||
192 | PVariable[] mappedVariables = extractMappedVariables(positivePatternCall); | ||
193 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
194 | addTrace(positivePatternCall, | ||
195 | new PositivePatternCall(body, variablesTuple, positivePatternCall.getReferredQuery())); | ||
196 | } | ||
197 | |||
198 | protected void copyNegativePatternCallConstraint(NegativePatternCall negativePatternCall) { | ||
199 | PVariable[] mappedVariables = extractMappedVariables(negativePatternCall); | ||
200 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
201 | addTrace(negativePatternCall, | ||
202 | new NegativePatternCall(body, variablesTuple, negativePatternCall.getReferredQuery())); | ||
203 | } | ||
204 | |||
205 | protected void copyBinaryTransitiveClosureConstraint(BinaryTransitiveClosure binaryTransitiveClosure) { | ||
206 | PVariable[] mappedVariables = extractMappedVariables(binaryTransitiveClosure); | ||
207 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
208 | addTrace(binaryTransitiveClosure, | ||
209 | new BinaryTransitiveClosure(body, variablesTuple, binaryTransitiveClosure.getReferredQuery())); | ||
210 | } | ||
211 | |||
212 | protected void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) { | ||
213 | var mappedVariables = extractMappedVariables(constraint); | ||
214 | var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
215 | addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(), | ||
216 | constraint.getConnectivity())); | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * @since 2.8 | ||
221 | */ | ||
222 | protected void copyRelationEvaluationConstraint(RelationEvaluation relationEvaluation) { | ||
223 | PVariable[] mappedVariables = extractMappedVariables(relationEvaluation); | ||
224 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
225 | addTrace(relationEvaluation, new RelationEvaluation(body, variablesTuple, relationEvaluation.getReferredQueries(), | ||
226 | relationEvaluation.getEvaluator())); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * @since 2.0 | ||
231 | */ | ||
232 | protected void copyBinaryReflexiveTransitiveClosureConstraint( | ||
233 | BinaryReflexiveTransitiveClosure binaryReflexiveTransitiveClosure) { | ||
234 | PVariable[] mappedVariables = extractMappedVariables(binaryReflexiveTransitiveClosure); | ||
235 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
236 | addTrace(binaryReflexiveTransitiveClosure, | ||
237 | new BinaryReflexiveTransitiveClosure(body, variablesTuple, | ||
238 | binaryReflexiveTransitiveClosure.getReferredQuery(), | ||
239 | binaryReflexiveTransitiveClosure.getUniverseType())); | ||
240 | } | ||
241 | |||
242 | protected void copyPatternMatchCounterConstraint(PatternMatchCounter patternMatchCounter) { | ||
243 | PVariable[] mappedVariables = extractMappedVariables(patternMatchCounter); | ||
244 | PVariable mappedResultVariable = variableMapping.get(patternMatchCounter.getResultVariable()); | ||
245 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
246 | addTrace(patternMatchCounter, new PatternMatchCounter(body, variablesTuple, | ||
247 | patternMatchCounter.getReferredQuery(), mappedResultVariable)); | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * @since 1.4 | ||
252 | */ | ||
253 | protected void copyAggregatorConstraint(AggregatorConstraint constraint) { | ||
254 | PVariable[] mappedVariables = extractMappedVariables(constraint); | ||
255 | PVariable mappedResultVariable = variableMapping.get(constraint.getResultVariable()); | ||
256 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
257 | addTrace(constraint, new AggregatorConstraint(constraint.getAggregator(), body, variablesTuple, | ||
258 | constraint.getReferredQuery(), mappedResultVariable, constraint.getAggregatedColumn())); | ||
259 | } | ||
260 | |||
261 | protected void copyExpressionEvaluationConstraint(ExpressionEvaluation expressionEvaluation) { | ||
262 | PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); | ||
263 | addTrace(expressionEvaluation, new ExpressionEvaluation(body, | ||
264 | new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), | ||
265 | mappedOutputVariable, expressionEvaluation.isUnwinding())); | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * For positive pattern calls | ||
270 | * | ||
271 | * @param positivePatternCall | ||
272 | * @return the mapped variables to the pattern's parameters | ||
273 | */ | ||
274 | protected PVariable[] extractMappedVariables(EnumerablePConstraint enumerablePConstraint) { | ||
275 | Object[] pVariables = enumerablePConstraint.getVariablesTuple().getElements(); | ||
276 | return mapVariableList(pVariables); | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * For negative and count pattern calls. | ||
281 | * | ||
282 | * @param patternMatchCounter | ||
283 | * @return the mapped variables to the pattern's parameters | ||
284 | */ | ||
285 | private PVariable[] extractMappedVariables(PatternCallBasedDeferred patternCallBasedDeferred) { | ||
286 | Object[] pVariables = patternCallBasedDeferred.getActualParametersTuple().getElements(); | ||
287 | return mapVariableList(pVariables); | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * For type filters. | ||
292 | */ | ||
293 | private PVariable[] extractMappedVariables(TypeFilterConstraint typeFilterConstraint) { | ||
294 | Object[] pVariables = typeFilterConstraint.getVariablesTuple().getElements(); | ||
295 | return mapVariableList(pVariables); | ||
296 | } | ||
297 | |||
298 | private PVariable[] mapVariableList(Object[] pVariables) { | ||
299 | List<PVariable> list = new ArrayList<PVariable>(); | ||
300 | for (int i = 0; i < pVariables.length; i++) { | ||
301 | PVariable mappedVariable = variableMapping.get(pVariables[i]); | ||
302 | list.add(mappedVariable); | ||
303 | } | ||
304 | return list.toArray(new PVariable[0]); | ||
305 | } | ||
306 | |||
307 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java new file mode 100644 index 00000000..90943129 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java | |||
@@ -0,0 +1,310 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro | ||
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.psystem.rewriters; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.Comparator; | ||
16 | import java.util.HashMap; | ||
17 | import java.util.HashSet; | ||
18 | import java.util.Iterator; | ||
19 | import java.util.LinkedHashSet; | ||
20 | import java.util.LinkedList; | ||
21 | import java.util.List; | ||
22 | import java.util.Map; | ||
23 | import java.util.Queue; | ||
24 | import java.util.Set; | ||
25 | |||
26 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
38 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
39 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
40 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
41 | |||
42 | /** | ||
43 | * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity | ||
44 | * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable | ||
45 | * disjunction. | ||
46 | * | ||
47 | * @author Gabor Bergmann | ||
48 | * | ||
49 | */ | ||
50 | public class PBodyNormalizer extends PDisjunctionRewriter { | ||
51 | |||
52 | private IQueryMetaContext context; | ||
53 | |||
54 | public PBodyNormalizer(IQueryMetaContext context) { | ||
55 | this.context = context; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass | ||
60 | * with a custom implementation. | ||
61 | * | ||
62 | * @since 1.6 | ||
63 | */ | ||
64 | protected boolean shouldCalculateImpliedTypes(PQuery query) { | ||
65 | return true; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints. | ||
70 | * This behavior can be customized by creating a subclass | ||
71 | * with a custom implementation. | ||
72 | * | ||
73 | * @since 1.6 | ||
74 | */ | ||
75 | protected boolean shouldExpandWeakenedAlternatives(PQuery query) { | ||
76 | return false; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
81 | Set<PBody> normalizedBodies = new LinkedHashSet<>(); | ||
82 | for (PBody body : disjunction.getBodies()) { | ||
83 | PBodyCopier copier = new PBodyCopier(body, getTraceCollector()); | ||
84 | PBody modifiedBody = copier.getCopiedBody(); | ||
85 | normalizeBody(modifiedBody); | ||
86 | normalizedBodies.add(modifiedBody); | ||
87 | modifiedBody.setStatus(PQueryStatus.OK); | ||
88 | } | ||
89 | return new PDisjunction(normalizedBodies); | ||
90 | } | ||
91 | |||
92 | public void setContext(IQueryMetaContext context) { | ||
93 | this.context = context; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Provides a normalized version of the pattern body. May return a different version than the original version if | ||
98 | * needed. | ||
99 | * | ||
100 | * @param body | ||
101 | */ | ||
102 | public PBody normalizeBody(PBody body) { | ||
103 | try { | ||
104 | return normalizeBodyInternal(body); | ||
105 | } catch (QueryProcessingException e) { | ||
106 | throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() }, | ||
107 | e.getShortMessage(), body.getPattern(), e); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | PBody normalizeBodyInternal(PBody body) { | ||
112 | // UNIFICATION AND WEAK INEQUALITY ELIMINATION | ||
113 | unifyVariablesAlongEqualities(body); | ||
114 | eliminateWeakInequalities(body); | ||
115 | removeMootEqualities(body); | ||
116 | |||
117 | // ADDING WEAKENED ALTERNATIVES | ||
118 | if (shouldExpandWeakenedAlternatives(body.getPattern())) { | ||
119 | expandWeakenedAlternativeConstraints(body); | ||
120 | } | ||
121 | |||
122 | // CONSTRAINT ELIMINATION WITH TYPE INFERENCE | ||
123 | if (shouldCalculateImpliedTypes(body.getPattern())) { | ||
124 | eliminateInferrableTypes(body, context); | ||
125 | } else { | ||
126 | // ELIMINATE DUPLICATE TYPE CONSTRAINTS | ||
127 | eliminateDuplicateTypeConstraints(body); | ||
128 | } | ||
129 | |||
130 | |||
131 | // PREVENTIVE CHECKS | ||
132 | checkSanity(body); | ||
133 | return body; | ||
134 | } | ||
135 | |||
136 | private void removeMootEqualities(PBody body) { | ||
137 | Set<Equality> equals = body.getConstraintsOfType(Equality.class); | ||
138 | for (Equality equality : equals) { | ||
139 | if (equality.isMoot()) { | ||
140 | equality.delete(); | ||
141 | derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Unifies allVariables along equalities so that they can be handled as one. | ||
148 | * | ||
149 | * @param body | ||
150 | */ | ||
151 | void unifyVariablesAlongEqualities(PBody body) { | ||
152 | Set<Equality> equals = body.getConstraintsOfType(Equality.class); | ||
153 | for (Equality equality : equals) { | ||
154 | if (!equality.isMoot()) { | ||
155 | equality.getWho().unifyInto(equality.getWithWhom()); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Eliminates weak inequalities if they are not substantiated. | ||
162 | * | ||
163 | * @param body | ||
164 | */ | ||
165 | void eliminateWeakInequalities(PBody body) { | ||
166 | for (Inequality inequality : body.getConstraintsOfType(Inequality.class)){ | ||
167 | if (inequality.isEliminable()){ | ||
168 | inequality.eliminateWeak(); | ||
169 | derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * Eliminates all type constraints that are inferrable from other constraints. | ||
176 | */ | ||
177 | void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) { | ||
178 | Set<TypeJudgement> subsumedByRetainedConstraints = new HashSet<TypeJudgement>(); | ||
179 | LinkedList<ITypeConstraint> allTypeConstraints = new LinkedList<ITypeConstraint>(); | ||
180 | for (PConstraint pConstraint : body.getConstraints()) { | ||
181 | if (pConstraint instanceof ITypeConstraint) { | ||
182 | allTypeConstraints.add((ITypeConstraint) pConstraint); | ||
183 | } else if (pConstraint instanceof ITypeInfoProviderConstraint) { | ||
184 | // non-type constraints are all retained | ||
185 | final Set<TypeJudgement> directJudgements = ((ITypeInfoProviderConstraint) pConstraint) | ||
186 | .getImpliedJudgements(context); | ||
187 | subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, directJudgements, | ||
188 | context); | ||
189 | } | ||
190 | } | ||
191 | Comparator<ITypeConstraint> eliminationOrder = (o1, o2) -> { | ||
192 | IInputKey type1 = o1.getEquivalentJudgement().getInputKey(); | ||
193 | IInputKey type2 = o2.getEquivalentJudgement().getInputKey(); | ||
194 | |||
195 | int result = context.getSuggestedEliminationOrdering().compare(type1, type2); | ||
196 | return (result == 0) | ||
197 | ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2) | ||
198 | : result; | ||
199 | }; | ||
200 | |||
201 | Collections.sort(allTypeConstraints, eliminationOrder); | ||
202 | Queue<ITypeConstraint> potentialConstraints = allTypeConstraints; // rename for better comprehension | ||
203 | |||
204 | while (!potentialConstraints.isEmpty()) { | ||
205 | ITypeConstraint candidate = potentialConstraints.poll(); | ||
206 | |||
207 | boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement()); | ||
208 | if (!isSubsumed) { | ||
209 | Set<TypeJudgement> typeClosure = subsumedByRetainedConstraints; | ||
210 | for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones | ||
211 | final Set<TypeJudgement> directJudgements = subsuming.getImpliedJudgements(context); | ||
212 | typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context); | ||
213 | |||
214 | if (typeClosure.contains(candidate.getEquivalentJudgement())) { | ||
215 | isSubsumed = true; | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | if (isSubsumed) { // eliminated | ||
221 | candidate.delete(); | ||
222 | derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED); | ||
223 | } else { // retained | ||
224 | subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, | ||
225 | candidate.getImpliedJudgements(context), context); | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan. | ||
232 | */ | ||
233 | void expandWeakenedAlternativeConstraints(PBody body) { | ||
234 | Set<TypeJudgement> allJudgements = new HashSet<TypeJudgement>(); | ||
235 | Set<TypeJudgement> newJudgementsToAdd = new HashSet<TypeJudgement>(); | ||
236 | Queue<TypeJudgement> judgementsToProcess = new LinkedList<TypeJudgement>(); | ||
237 | Map<TypeJudgement, List<PConstraint>> traceability = CollectionsFactory.createMap(); | ||
238 | |||
239 | for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) { | ||
240 | TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement(); | ||
241 | judgementsToProcess.add(equivalentJudgement); | ||
242 | allJudgements.add(equivalentJudgement); | ||
243 | traceability.computeIfAbsent(equivalentJudgement, k-> new ArrayList<>()).add(typeConstraint); | ||
244 | } | ||
245 | |||
246 | while (!judgementsToProcess.isEmpty()) { | ||
247 | TypeJudgement judgement = judgementsToProcess.poll(); | ||
248 | for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) { | ||
249 | if (allJudgements.add(alternativeJudgement)) { | ||
250 | newJudgementsToAdd.add(alternativeJudgement); | ||
251 | judgementsToProcess.add(alternativeJudgement); | ||
252 | traceability.merge( | ||
253 | alternativeJudgement, | ||
254 | traceability.getOrDefault(judgement, new ArrayList<>()), | ||
255 | (old,further) -> {old.addAll(further); return old;} | ||
256 | ); | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | for (TypeJudgement typeJudgement : newJudgementsToAdd) { | ||
262 | PConstraint newConstraint = typeJudgement.createConstraintFor(body); | ||
263 | for (PConstraint source : traceability.getOrDefault(typeJudgement, Collections.emptyList())) { | ||
264 | addTrace(source, newConstraint); | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | private Object getConstraintKey(PConstraint constraint) { | ||
270 | if (constraint instanceof ITypeConstraint) { | ||
271 | return ((ITypeConstraint) constraint).getEquivalentJudgement(); | ||
272 | } | ||
273 | // Do not check duplication for any other types | ||
274 | return constraint; | ||
275 | } | ||
276 | |||
277 | void eliminateDuplicateTypeConstraints(PBody body) { | ||
278 | Map<Object, PConstraint> constraints = new HashMap<>(); | ||
279 | for (PConstraint constraint : body.getConstraints()) { | ||
280 | Object key = getConstraintKey(constraint); | ||
281 | // Retain first found instance of a constraint | ||
282 | if (!constraints.containsKey(key)) { | ||
283 | constraints.put(key, constraint); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | // Retain collected constraints, remove everything else | ||
288 | Iterator<PConstraint> iterator = body.getConstraints().iterator(); | ||
289 | Collection<PConstraint> toRetain = constraints.values(); | ||
290 | while(iterator.hasNext()){ | ||
291 | PConstraint next = iterator.next(); | ||
292 | if (!toRetain.contains(next)){ | ||
293 | derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE); | ||
294 | iterator.remove(); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting. | ||
301 | * | ||
302 | * @param body | ||
303 | * @throws RetePatternBuildException | ||
304 | */ | ||
305 | void checkSanity(PBody body) { | ||
306 | for (PConstraint pConstraint : body.getConstraints()) | ||
307 | pConstraint.checkSanity(); | ||
308 | } | ||
309 | |||
310 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java new file mode 100644 index 00000000..c844ccf7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * An abstract base class for creating alternative representations for PDisjunctions. | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * | ||
18 | */ | ||
19 | public abstract class PDisjunctionRewriter extends AbstractRewriterTraceSource{ | ||
20 | |||
21 | public abstract PDisjunction rewrite(PDisjunction disjunction); | ||
22 | |||
23 | public PDisjunction rewrite(PQuery query) { | ||
24 | return rewrite(query.getDisjunctBodies()); | ||
25 | } | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java new file mode 100644 index 00000000..eb5422ca --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.WeakHashMap; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
18 | |||
19 | /** | ||
20 | * A rewriter that stores the previously computed results of a rewriter or a rewriter chain. | ||
21 | * | ||
22 | * @author Zoltan Ujhelyi | ||
23 | * @since 1.0 | ||
24 | */ | ||
25 | public class PDisjunctionRewriterCacher extends PDisjunctionRewriter { | ||
26 | |||
27 | private final List<PDisjunctionRewriter> rewriterChain; | ||
28 | private WeakHashMap<PDisjunction, PDisjunction> cachedResults = | ||
29 | new WeakHashMap<PDisjunction, PDisjunction>(); | ||
30 | |||
31 | private void setupTraceCollectorInChain(){ | ||
32 | IRewriterTraceCollector collector = getTraceCollector(); | ||
33 | for(PDisjunctionRewriter rewriter: rewriterChain){ | ||
34 | rewriter.setTraceCollector(collector); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public PDisjunctionRewriterCacher(PDisjunctionRewriter rewriter) { | ||
39 | rewriterChain = Collections.singletonList(rewriter); | ||
40 | } | ||
41 | |||
42 | public PDisjunctionRewriterCacher(PDisjunctionRewriter... rewriters) { | ||
43 | rewriterChain = new ArrayList<>(Arrays.asList(rewriters)); | ||
44 | } | ||
45 | |||
46 | public PDisjunctionRewriterCacher(List<PDisjunctionRewriter> rewriterChain) { | ||
47 | this.rewriterChain = new ArrayList<>(rewriterChain); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
52 | if (!cachedResults.containsKey(disjunction)) { | ||
53 | PDisjunction rewritten = disjunction; | ||
54 | setupTraceCollectorInChain(); | ||
55 | for (PDisjunctionRewriter rewriter : rewriterChain) { | ||
56 | rewritten = rewriter.rewrite(rewritten); | ||
57 | } | ||
58 | |||
59 | cachedResults.put(disjunction, rewritten); | ||
60 | } | ||
61 | return cachedResults.get(disjunction); | ||
62 | } | ||
63 | |||
64 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java new file mode 100644 index 00000000..76311d8f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java | |||
@@ -0,0 +1,253 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.ArrayDeque; | ||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Deque; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.LinkedHashSet; | ||
18 | import java.util.LinkedList; | ||
19 | import java.util.List; | ||
20 | import java.util.Map; | ||
21 | import java.util.Set; | ||
22 | |||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
33 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
34 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
35 | |||
36 | /** | ||
37 | * This rewriter class holds the query flattening logic | ||
38 | * | ||
39 | * @author Marton Bur | ||
40 | * | ||
41 | */ | ||
42 | public class PQueryFlattener extends PDisjunctionRewriter { | ||
43 | |||
44 | /** | ||
45 | * Utility function to produce the permutation of every possible mapping of values. | ||
46 | * | ||
47 | * @param values | ||
48 | * @return | ||
49 | */ | ||
50 | private static <K, V> Set<Map<K, V>> permutation(Map<K, Set<V>> values) { | ||
51 | // An ordering of keys is defined here which will help restoring the appropriate values after the execution of | ||
52 | // the cartesian product | ||
53 | List<K> keyList = new ArrayList<>(values.keySet()); | ||
54 | |||
55 | // Produce list of value sets with the ordering defined by keyList | ||
56 | List<Set<V>> valuesList = new ArrayList<Set<V>>(keyList.size()); | ||
57 | for (K key : keyList) { | ||
58 | valuesList.add(values.get(key)); | ||
59 | } | ||
60 | |||
61 | // Cartesian product will obey ordering of the list | ||
62 | Set<List<V>> valueMappings = Sets.cartesianProduct(valuesList); | ||
63 | |||
64 | // Build result | ||
65 | Set<Map<K, V>> result = new LinkedHashSet<>(); | ||
66 | for (List<V> valueList : valueMappings) { | ||
67 | Map<K, V> map = new HashMap<>(); | ||
68 | for (int i = 0; i < keyList.size(); i++) { | ||
69 | map.put(keyList.get(i), valueList.get(i)); | ||
70 | } | ||
71 | result.add(map); | ||
72 | } | ||
73 | |||
74 | return result; | ||
75 | } | ||
76 | |||
77 | private IFlattenCallPredicate flattenCallPredicate; | ||
78 | |||
79 | public PQueryFlattener(IFlattenCallPredicate flattenCallPredicate) { | ||
80 | this.flattenCallPredicate = flattenCallPredicate; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
85 | PQuery query = disjunction.getQuery(); | ||
86 | |||
87 | // Check for recursion | ||
88 | Set<PQuery> allReferredQueries = disjunction.getAllReferredQueries(); | ||
89 | for (PQuery referredQuery : allReferredQueries) { | ||
90 | if (referredQuery.getAllReferredQueries().contains(referredQuery)) { | ||
91 | throw new RewriterException("Recursive queries are not supported, can't flatten query named \"{1}\"", | ||
92 | new String[] { query.getFullyQualifiedName() }, "Unsupported recursive query", query); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | return this.doFlatten(disjunction); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Return the list of dependencies (including the root) in chronological order | ||
101 | * | ||
102 | * @param rootDisjunction | ||
103 | * @return | ||
104 | */ | ||
105 | private List<PDisjunction> disjunctionDependencies(PDisjunction rootDisjunction) { | ||
106 | // Disjunctions are first collected into a list usign a depth-first approach, | ||
107 | // which can be iterated backwards while removing duplicates | ||
108 | Deque<PDisjunction> stack = new ArrayDeque<>(); | ||
109 | LinkedList<PDisjunction> list = new LinkedList<>(); | ||
110 | stack.push(rootDisjunction); | ||
111 | list.add(rootDisjunction); | ||
112 | |||
113 | while (!stack.isEmpty()) { | ||
114 | PDisjunction disjunction = stack.pop(); | ||
115 | // Collect dependencies | ||
116 | for (PBody pBody : disjunction.getBodies()) { | ||
117 | for (PConstraint constraint : pBody.getConstraints()) { | ||
118 | if (constraint instanceof PositivePatternCall) { | ||
119 | PositivePatternCall positivePatternCall = (PositivePatternCall) constraint; | ||
120 | if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { | ||
121 | // If the above preconditions meet, the call should be flattened | ||
122 | PDisjunction calledDisjunction = positivePatternCall.getReferredQuery().getDisjunctBodies(); | ||
123 | stack.push(calledDisjunction); | ||
124 | list.add(calledDisjunction); | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | // Remove duplicates (keeping the last instance) and reverse order | ||
132 | Set<PDisjunction> visited = new HashSet<PDisjunction>(); | ||
133 | List<PDisjunction> result = new ArrayList<PDisjunction>(list.size()); | ||
134 | |||
135 | list.descendingIterator().forEachRemaining(item -> { | ||
136 | if (!visited.contains(item)) { | ||
137 | result.add(item); | ||
138 | visited.add(item); | ||
139 | } | ||
140 | |||
141 | }); | ||
142 | |||
143 | return result; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * This function holds the actual flattening logic for a PQuery | ||
148 | * | ||
149 | * @param rootDisjunction | ||
150 | * to be flattened | ||
151 | * @return the flattened bodies of the pQuery | ||
152 | */ | ||
153 | private PDisjunction doFlatten(PDisjunction rootDisjunction) { | ||
154 | |||
155 | Map<PDisjunction, Set<PBody>> flatBodyMapping = new HashMap<>(); | ||
156 | |||
157 | List<PDisjunction> dependencies = disjunctionDependencies(rootDisjunction); | ||
158 | |||
159 | for (PDisjunction disjunction : dependencies) { | ||
160 | Set<PBody> flatBodies = new LinkedHashSet<>(); | ||
161 | for (PBody body : disjunction.getBodies()) { | ||
162 | if (isFlatteningNeeded(body)) { | ||
163 | Map<PositivePatternCall, Set<PBody>> flattenedBodies = new HashMap<>(); | ||
164 | for (PConstraint pConstraint : body.getConstraints()) { | ||
165 | |||
166 | if (pConstraint instanceof PositivePatternCall) { | ||
167 | PositivePatternCall positivePatternCall = (PositivePatternCall) pConstraint; | ||
168 | if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { | ||
169 | // If the above preconditions meet, do the flattening and return the disjoint bodies | ||
170 | PDisjunction calledDisjunction = positivePatternCall.getReferredQuery() | ||
171 | .getDisjunctBodies(); | ||
172 | |||
173 | Set<PBody> flattenedBodySet = flatBodyMapping.get(calledDisjunction); | ||
174 | Preconditions.checkArgument(!flattenedBodySet.isEmpty()); | ||
175 | flattenedBodies.put(positivePatternCall, flattenedBodySet); | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | flatBodies.addAll(createSetOfFlatPBodies(body, flattenedBodies)); | ||
180 | } else { | ||
181 | flatBodies.add(prepareFlatPBody(body)); | ||
182 | } | ||
183 | } | ||
184 | flatBodyMapping.put(disjunction, flatBodies); | ||
185 | } | ||
186 | |||
187 | return new PDisjunction(rootDisjunction.getQuery(), flatBodyMapping.get(rootDisjunction)); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Creates the flattened bodies based on the caller body and the called (and already flattened) disjunctions | ||
192 | * | ||
193 | * @param pBody | ||
194 | * the body to flatten | ||
195 | * @param flattenedDisjunctions | ||
196 | * the | ||
197 | * @param flattenedCalls | ||
198 | * @return | ||
199 | */ | ||
200 | private Set<PBody> createSetOfFlatPBodies(PBody pBody, Map<PositivePatternCall, Set<PBody>> flattenedCalls) { | ||
201 | PQuery pQuery = pBody.getPattern(); | ||
202 | |||
203 | Set<Map<PositivePatternCall, PBody>> conjunctedCalls = permutation(flattenedCalls); | ||
204 | |||
205 | // The result set containing the merged conjuncted bodies | ||
206 | Set<PBody> conjunctedBodies = new HashSet<>(); | ||
207 | |||
208 | for (Map<PositivePatternCall, PBody> calledBodies : conjunctedCalls) { | ||
209 | FlattenerCopier copier = createBodyCopier(pQuery, calledBodies); | ||
210 | |||
211 | int i = 0; | ||
212 | HierarchicalName hierarchicalNamingTool = new HierarchicalName(); | ||
213 | for (PositivePatternCall patternCall : calledBodies.keySet()) { | ||
214 | // Merge each called body | ||
215 | hierarchicalNamingTool.setCallCount(i++); | ||
216 | copier.mergeBody(patternCall, hierarchicalNamingTool, new ExportedParameterFilter()); | ||
217 | } | ||
218 | |||
219 | // Merge the caller's constraints to the conjunct body | ||
220 | copier.mergeBody(pBody); | ||
221 | |||
222 | PBody copiedBody = copier.getCopiedBody(); | ||
223 | copiedBody.setStatus(PQueryStatus.OK); | ||
224 | conjunctedBodies.add(copiedBody); | ||
225 | } | ||
226 | |||
227 | return conjunctedBodies; | ||
228 | } | ||
229 | |||
230 | private FlattenerCopier createBodyCopier(PQuery query, Map<PositivePatternCall, PBody> calledBodies) { | ||
231 | FlattenerCopier flattenerCopier = new FlattenerCopier(query, calledBodies); | ||
232 | flattenerCopier.setTraceCollector(getTraceCollector()); | ||
233 | return flattenerCopier; | ||
234 | } | ||
235 | |||
236 | private PBody prepareFlatPBody(PBody pBody) { | ||
237 | PBodyCopier copier = createBodyCopier(pBody.getPattern(), Collections.<PositivePatternCall, PBody> emptyMap()); | ||
238 | copier.mergeBody(pBody, new SameName(), new AllowAllFilter()); | ||
239 | // the copying of the body here is necessary for only one containing PDisjunction can be assigned to a PBody | ||
240 | return copier.getCopiedBody(); | ||
241 | } | ||
242 | |||
243 | private boolean isFlatteningNeeded(PBody pBody) { | ||
244 | // Check if the body contains positive pattern call AND if it should be flattened | ||
245 | for (PConstraint pConstraint : pBody.getConstraints()) { | ||
246 | if (pConstraint instanceof PositivePatternCall) { | ||
247 | return flattenCallPredicate.shouldFlatten((PositivePatternCall) pConstraint); | ||
248 | } | ||
249 | } | ||
250 | return false; | ||
251 | } | ||
252 | |||
253 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java new file mode 100644 index 00000000..d0fc286b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; | ||
12 | |||
13 | /** | ||
14 | * An exception to wrap various issues during PDisjunction rewriting. | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * | ||
17 | */ | ||
18 | public class RewriterException extends QueryInitializationException { | ||
19 | |||
20 | private static final long serialVersionUID = -4703825954995497932L; | ||
21 | |||
22 | public RewriterException(String message, String[] context, String shortMessage, Object patternDescription, | ||
23 | Throwable cause) { | ||
24 | super(message, context, shortMessage, patternDescription, cause); | ||
25 | } | ||
26 | |||
27 | public RewriterException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
28 | super(message, context, shortMessage, patternDescription); | ||
29 | } | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java new file mode 100644 index 00000000..71459558 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java | |||
@@ -0,0 +1,63 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.psystem.rewriters; | ||
10 | |||
11 | import java.util.LinkedHashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Zoltan Ujhelyi | ||
28 | * | ||
29 | */ | ||
30 | public class SurrogateQueryRewriter extends PDisjunctionRewriter { | ||
31 | |||
32 | @Override | ||
33 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
34 | Set<PBody> replacedBodies = new LinkedHashSet<>(); | ||
35 | for (PBody body : disjunction.getBodies()) { | ||
36 | PBodyCopier copier = new PBodyCopier(body, getTraceCollector()) { | ||
37 | |||
38 | @Override | ||
39 | protected void copyTypeConstraint(TypeConstraint typeConstraint) { | ||
40 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
41 | Tuple variablesTuple = Tuples.flatTupleOf((Object[])mappedVariables); | ||
42 | final IInputKey supplierKey = typeConstraint.getSupplierKey(); | ||
43 | if(SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) { | ||
44 | PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey); | ||
45 | if (surrogateQuery == null) { | ||
46 | throw new IllegalStateException( | ||
47 | String.format("Surrogate query for feature %s not found", | ||
48 | supplierKey.getPrettyPrintableName())); | ||
49 | } | ||
50 | addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple, surrogateQuery)); | ||
51 | } else { | ||
52 | addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey)); | ||
53 | } | ||
54 | } | ||
55 | }; | ||
56 | PBody modifiedBody = copier.getCopiedBody(); | ||
57 | replacedBodies.add(modifiedBody); | ||
58 | modifiedBody.setStatus(PQueryStatus.OK); | ||
59 | } | ||
60 | return new PDisjunction(replacedBodies); | ||
61 | } | ||
62 | |||
63 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java new file mode 100644 index 00000000..10337979 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.psystem.rewriters; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.LinkedHashMap; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | |||
20 | /** | ||
21 | * A wrapper for {@link IExpressionEvaluator} which is capable of correctly mapping variable names used by the | ||
22 | * expression. | ||
23 | * | ||
24 | * @author Grill Balázs | ||
25 | * | ||
26 | */ | ||
27 | class VariableMappingExpressionEvaluatorWrapper implements IExpressionEvaluator { | ||
28 | |||
29 | private final IExpressionEvaluator wrapped; | ||
30 | private final Map<String, String> variableMapping; | ||
31 | |||
32 | public VariableMappingExpressionEvaluatorWrapper(IExpressionEvaluator wrapped, | ||
33 | Map<PVariable, PVariable> variableMapping) { | ||
34 | |||
35 | // Support to rewrap an already wrapped expression. | ||
36 | boolean rewrap = wrapped instanceof VariableMappingExpressionEvaluatorWrapper; | ||
37 | this.wrapped = rewrap ? ((VariableMappingExpressionEvaluatorWrapper)wrapped).wrapped : wrapped; | ||
38 | |||
39 | // Instead of just saving the reference of the mapping, save the actual (trimmed) state of the mapping as it | ||
40 | // may change during copying (especially during flattening). A LinkedHashMap is used to retain ordering of | ||
41 | // original parameter names iterator. | ||
42 | this.variableMapping = new LinkedHashMap<>(); | ||
43 | |||
44 | // Index map by variable names | ||
45 | Map<String, PVariable> names = new HashMap<>(); | ||
46 | for (PVariable originalVar : variableMapping.keySet()) { | ||
47 | names.put(originalVar.getName(), originalVar); | ||
48 | } | ||
49 | |||
50 | // In case of rewrapping, current names are contained by the previous mapping | ||
51 | Map<String, String> previousMapping = null; | ||
52 | if (rewrap){ | ||
53 | previousMapping = ((VariableMappingExpressionEvaluatorWrapper)wrapped).variableMapping; | ||
54 | } | ||
55 | |||
56 | // Populate mapping | ||
57 | for (String inputParameterName : this.wrapped.getInputParameterNames()) { | ||
58 | String parameterName = rewrap ? previousMapping.get(inputParameterName) : inputParameterName; | ||
59 | Preconditions.checkArgument(parameterName != null); | ||
60 | PVariable original = names.get(parameterName); | ||
61 | Preconditions.checkArgument(original != null); | ||
62 | PVariable mapped = variableMapping.get(original); | ||
63 | if (mapped != null){ | ||
64 | this.variableMapping.put(inputParameterName, mapped.getName()); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public String getShortDescription() { | ||
71 | return wrapped.getShortDescription(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public Iterable<String> getInputParameterNames() { | ||
76 | return variableMapping.values(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public Object evaluateExpression(final IValueProvider provider) throws Exception { | ||
81 | return wrapped.evaluateExpression(variableName -> { | ||
82 | String mappedVariableName = variableMapping.get(variableName); | ||
83 | Preconditions.checkArgument(mappedVariableName != null, "Could not find variable %s", variableName); | ||
84 | return provider.getValue(mappedVariableName); | ||
85 | }); | ||
86 | } | ||
87 | |||
88 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java new file mode 100644 index 00000000..a26d9193 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java | |||
@@ -0,0 +1,136 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.tuple; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Objects; | ||
17 | import java.util.Set; | ||
18 | |||
19 | /** | ||
20 | * Common implementation methods for immutable and volatile tuples. The class should not be used directly in client | ||
21 | * code, except for the definition of new tuple implementations. | ||
22 | * | ||
23 | * @author Zoltan Ujhelyi | ||
24 | * @since 1.7 | ||
25 | */ | ||
26 | public abstract class AbstractTuple implements ITuple { | ||
27 | |||
28 | /** | ||
29 | * As the tuple is supposed to be immutable, do not modify the returned array. | ||
30 | * | ||
31 | * @return the array containing all elements of this Tuple | ||
32 | */ | ||
33 | @Override | ||
34 | public Object[] getElements() { | ||
35 | Object[] allElements = new Object[getSize()]; | ||
36 | for (int i = 0; i < allElements.length; ++i) | ||
37 | allElements[i] = get(i); | ||
38 | return allElements; | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @return the set containing all distinct elements of this Tuple, cast as type T | ||
43 | */ | ||
44 | @Override | ||
45 | @SuppressWarnings("unchecked") | ||
46 | public <T> Set<T> getDistinctElements() { | ||
47 | Set<T> result = new HashSet<T>(); | ||
48 | Object[] elements = getElements(); | ||
49 | for (Object object : elements) { | ||
50 | result.add((T) object); | ||
51 | } | ||
52 | return result; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) | ||
57 | * occurrence is calculated. | ||
58 | * | ||
59 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
60 | */ | ||
61 | @Override | ||
62 | public Map<Object, Integer> invertIndex() { | ||
63 | Map<Object, Integer> result = new HashMap<Object, Integer>(); | ||
64 | for (int i = 0; i < getSize(); i++) | ||
65 | result.put(get(i), i); | ||
66 | return result; | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its | ||
71 | * occurrences is calculated. | ||
72 | * | ||
73 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
74 | */ | ||
75 | @Override | ||
76 | public Map<Object, List<Integer>> invertIndexWithMupliplicity() { | ||
77 | Map<Object, List<Integer>> result = new HashMap<Object, List<Integer>>(); | ||
78 | for (int i = 0; i < getSize(); i++) { | ||
79 | Object value = get(i); | ||
80 | List<Integer> indices = result.computeIfAbsent(value, v -> new ArrayList<>()); | ||
81 | indices.add(i); | ||
82 | } | ||
83 | return result; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * @since 1.7 | ||
88 | */ | ||
89 | protected IndexOutOfBoundsException raiseIndexingError(int index) { | ||
90 | return new IndexOutOfBoundsException( | ||
91 | String.format("No value at position %d for %s instance %s", index, getClass().getSimpleName(), this)); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Compares the elements stored in this tuple to another tuple | ||
96 | */ | ||
97 | protected boolean internalEquals(ITuple other) { | ||
98 | if (getSize() != other.getSize()) | ||
99 | return false; | ||
100 | boolean result = true; | ||
101 | for (int i = 0; result && i < getSize(); ++i) { | ||
102 | Object ours = get(i); | ||
103 | Object theirs = other.get(i); | ||
104 | result = result && Objects.equals(ours, theirs); | ||
105 | } | ||
106 | return result; | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public String toString() { | ||
111 | StringBuilder s = new StringBuilder(); | ||
112 | s.append("T("); | ||
113 | for (Object o : getElements()) { | ||
114 | s.append(o == null ? "null" : o.toString()); | ||
115 | s.append(';'); | ||
116 | } | ||
117 | s.append(')'); | ||
118 | return s.toString(); | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * @since 1.7 | ||
123 | */ | ||
124 | protected int doCalcHash() { | ||
125 | final int PRIME = 31; | ||
126 | int hash = 1; | ||
127 | for (int i = 0; i < getSize(); i++) { | ||
128 | hash = PRIME * hash; | ||
129 | Object element = get(i); | ||
130 | if (element != null) | ||
131 | hash += element.hashCode(); | ||
132 | } | ||
133 | return hash; | ||
134 | } | ||
135 | |||
136 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java new file mode 100644 index 00000000..6f46b763 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java | |||
@@ -0,0 +1,20 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Base class for all flat tuple implementations. | ||
13 | * Flat tuples store all elements locally (do not reference other tuples). | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public abstract class BaseFlatTuple extends Tuple { | ||
19 | |||
20 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java new file mode 100644 index 00000000..03f9ea89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Common functionality of left inheritance tuple implementations. | ||
13 | * | ||
14 | * <p>Left inheritance tuples inherit their first few elements from another tuple, | ||
15 | * and extend it with additional "local" elements. | ||
16 | * | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.7 | ||
19 | */ | ||
20 | public abstract class BaseLeftInheritanceTuple extends Tuple { | ||
21 | |||
22 | /** | ||
23 | * The number of elements that aren't stored locally, but inherited from an ancestor Tuple instead. | ||
24 | */ | ||
25 | protected final int inheritedIndex; | ||
26 | /** | ||
27 | * This object contains the same elements as the ancestor on the first inheritedIndex positions | ||
28 | */ | ||
29 | protected final Tuple ancestor; | ||
30 | |||
31 | /** | ||
32 | * @param ancestor | ||
33 | */ | ||
34 | public BaseLeftInheritanceTuple(Tuple ancestor) { | ||
35 | super(); | ||
36 | this.ancestor = ancestor; | ||
37 | this.inheritedIndex = ancestor.getSize(); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @return the number of local (non-inherited) elements | ||
42 | */ | ||
43 | public abstract int getLocalSize(); | ||
44 | |||
45 | /** | ||
46 | * Optimized equals calculation (prediction: true, since hash values match) | ||
47 | */ | ||
48 | @Override | ||
49 | protected boolean internalEquals(ITuple other) { | ||
50 | if (other instanceof BaseLeftInheritanceTuple) { | ||
51 | BaseLeftInheritanceTuple blit = (BaseLeftInheritanceTuple) other; | ||
52 | if (blit.inheritedIndex == this.inheritedIndex) { | ||
53 | if (this.ancestor.equals(blit.ancestor)) { | ||
54 | return localEquals(blit); | ||
55 | } else return false; | ||
56 | } | ||
57 | } | ||
58 | return super.internalEquals(other); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Checks the equivalence of local elements only, after ancestor tuple has been determined to be equal. | ||
63 | */ | ||
64 | protected abstract boolean localEquals(BaseLeftInheritanceTuple other); | ||
65 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java new file mode 100644 index 00000000..8bbb0ac2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | |||
14 | /** | ||
15 | * Default Tuple implementation, with statically unknown arity. | ||
16 | * @author Gabor Bergmann | ||
17 | */ | ||
18 | public final class FlatTuple extends BaseFlatTuple { | ||
19 | |||
20 | /** | ||
21 | * Array of substituted values. DO NOT MODIFY! Use Constructor to build a new instance instead. | ||
22 | */ | ||
23 | private final Object[] elements; | ||
24 | |||
25 | /** | ||
26 | * Creates a FlatTuple instance, fills it with the given array. | ||
27 | * <p> Users should consider calling {@link Tuples#flatTupleOf(Object...)} instead to save memory on low-arity tuples. | ||
28 | * | ||
29 | * @param elements | ||
30 | * array of substitution values | ||
31 | */ | ||
32 | protected FlatTuple(Object... elements) { | ||
33 | this.elements = Arrays.copyOf(elements, elements.length); | ||
34 | calcHash(); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Object get(int index) { | ||
39 | return elements[index]; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public int getSize() { | ||
44 | return elements.length; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public Object[] getElements() { | ||
49 | return elements; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | protected boolean internalEquals(ITuple other) { | ||
54 | if (other instanceof FlatTuple) { | ||
55 | return Arrays.equals(elements, ((FlatTuple) other).elements); | ||
56 | } else | ||
57 | return super.internalEquals(other); | ||
58 | } | ||
59 | |||
60 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java new file mode 100644 index 00000000..93f412b7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Flat tuple with statically known arity of 0. | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | * | ||
17 | */ | ||
18 | public final class FlatTuple0 extends BaseFlatTuple { | ||
19 | protected static final FlatTuple0 INSTANCE = new FlatTuple0(); | ||
20 | |||
21 | private FlatTuple0() { | ||
22 | calcHash(); | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public int getSize() { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Object get(int index) { | ||
32 | throw raiseIndexingError(index); | ||
33 | } | ||
34 | |||
35 | private static final Object[] NULLARY_ARRAY = new Object[0]; | ||
36 | |||
37 | @Override | ||
38 | public Object[] getElements() { | ||
39 | return NULLARY_ARRAY; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | protected boolean internalEquals(ITuple other) { | ||
44 | return 0 == other.getSize(); | ||
45 | } | ||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java new file mode 100644 index 00000000..b3b1c312 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 1. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple1 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | |||
23 | protected FlatTuple1(Object element0) { | ||
24 | this.element0 = element0; | ||
25 | calcHash(); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getSize() { | ||
30 | return 1; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public Object get(int index) { | ||
35 | if (index == 0) return element0; | ||
36 | else throw raiseIndexingError(index); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected boolean internalEquals(ITuple other) { | ||
41 | return 1 == other.getSize() && | ||
42 | Objects.equals(element0, other.get(0)); | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java new file mode 100644 index 00000000..2dcfd718 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 2. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple2 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | |||
24 | protected FlatTuple2(Object element0, Object element1) { | ||
25 | this.element0 = element0; | ||
26 | this.element1 = element1; | ||
27 | calcHash(); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public int getSize() { | ||
32 | return 2; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Object get(int index) { | ||
37 | switch(index) { | ||
38 | case 0 : return element0; | ||
39 | case 1 : return element1; | ||
40 | default: throw raiseIndexingError(index); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | protected boolean internalEquals(ITuple other) { | ||
46 | return 2 == other.getSize() && | ||
47 | Objects.equals(element0, other.get(0)) && | ||
48 | Objects.equals(element1, other.get(1)); | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java new file mode 100644 index 00000000..50cee57e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 3. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple3 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | private final Object element2; | ||
24 | |||
25 | protected FlatTuple3(Object element0, Object element1, Object element2) { | ||
26 | this.element0 = element0; | ||
27 | this.element1 = element1; | ||
28 | this.element2 = element2; | ||
29 | calcHash(); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public int getSize() { | ||
34 | return 3; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Object get(int index) { | ||
39 | switch (index) { | ||
40 | case 0: return element0; | ||
41 | case 1: return element1; | ||
42 | case 2: return element2; | ||
43 | default: throw raiseIndexingError(index); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected boolean internalEquals(ITuple other) { | ||
49 | return 3 == other.getSize() && | ||
50 | Objects.equals(element0, other.get(0)) && | ||
51 | Objects.equals(element1, other.get(1)) && | ||
52 | Objects.equals(element2, other.get(2)); | ||
53 | } | ||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java new file mode 100644 index 00000000..024c94f4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 4. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple4 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | private final Object element2; | ||
24 | private final Object element3; | ||
25 | |||
26 | protected FlatTuple4(Object element0, Object element1, Object element2, Object element3) { | ||
27 | this.element0 = element0; | ||
28 | this.element1 = element1; | ||
29 | this.element2 = element2; | ||
30 | this.element3 = element3; | ||
31 | calcHash(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int getSize() { | ||
36 | return 4; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Object get(int index) { | ||
41 | switch(index) { | ||
42 | case 0: return element0; | ||
43 | case 1: return element1; | ||
44 | case 2: return element2; | ||
45 | case 3: return element3; | ||
46 | default: throw raiseIndexingError(index); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | protected boolean internalEquals(ITuple other) { | ||
52 | return 4 == other.getSize() && | ||
53 | Objects.equals(element0, other.get(0)) && | ||
54 | Objects.equals(element1, other.get(1)) && | ||
55 | Objects.equals(element2, other.get(2)) && | ||
56 | Objects.equals(element3, other.get(3)); | ||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java new file mode 100644 index 00000000..f5dab2a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.tuple; | ||
10 | |||
11 | /** | ||
12 | * A tuple that allows modifying the underlying value. Should not be used for non-volatile tuples. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public interface IModifiableTuple extends ITuple { | ||
18 | |||
19 | /** | ||
20 | * Sets the selected value for a tuple | ||
21 | * | ||
22 | * @pre: 0 <= index < getSize() | ||
23 | * | ||
24 | */ | ||
25 | void set(int index, Object value); | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java new file mode 100644 index 00000000..92014781 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * Represents both mutable and immutable tuples | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * @since 1.7 | ||
20 | * | ||
21 | */ | ||
22 | public interface ITuple { | ||
23 | |||
24 | /** | ||
25 | * @pre: 0 <= index < getSize() | ||
26 | * | ||
27 | * @return the element at the specified index | ||
28 | */ | ||
29 | Object get(int index); | ||
30 | |||
31 | /** | ||
32 | * As the tuple is supposed to be immutable, do not modify the returned array. | ||
33 | * @return the array containing all elements of this Tuple | ||
34 | */ | ||
35 | Object[] getElements(); | ||
36 | |||
37 | /** | ||
38 | * @return the set containing all distinct elements of this Tuple, cast as type T | ||
39 | */ | ||
40 | <T> Set<T> getDistinctElements(); | ||
41 | |||
42 | /** | ||
43 | * @return number of elements | ||
44 | */ | ||
45 | int getSize(); | ||
46 | |||
47 | /** | ||
48 | * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) | ||
49 | * occurrence is calculated. | ||
50 | * | ||
51 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
52 | */ | ||
53 | Map<Object, Integer> invertIndex(); | ||
54 | |||
55 | /** | ||
56 | * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its | ||
57 | * occurrences is calculated. | ||
58 | * | ||
59 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
60 | */ | ||
61 | Map<Object, List<Integer>> invertIndexWithMupliplicity(); | ||
62 | |||
63 | Tuple toImmutable(); | ||
64 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java new file mode 100644 index 00000000..dcdfc376 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java | |||
@@ -0,0 +1,172 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Objects; | ||
14 | |||
15 | /** | ||
16 | * | ||
17 | * Tuple that inherits another tuple on the left. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public final class LeftInheritanceTuple extends BaseLeftInheritanceTuple { | ||
23 | /** | ||
24 | * Array of substituted values above inheritedIndex. DO NOT MODIFY! Use Constructor to build a new instance instead. | ||
25 | */ | ||
26 | private final Object[] localElements; | ||
27 | |||
28 | // | ||
29 | // /** | ||
30 | // * Creates a Tuple instance, fills it with the given array. | ||
31 | // * @pre: no elements are null | ||
32 | // * @param elements array of substitution values | ||
33 | // */ | ||
34 | // public Tuple(Object[] elements) | ||
35 | // { | ||
36 | // this.localElements = elements; | ||
37 | // this.ancestor=null; | ||
38 | // this.inheritedIndex = 0; | ||
39 | // calcHash(); | ||
40 | // } | ||
41 | |||
42 | |||
43 | |||
44 | /** | ||
45 | * Creates a Tuple instance, lets it inherit from an ancestor, extends it with a given array. @pre: no elements are | ||
46 | * null | ||
47 | * | ||
48 | * @param localElements | ||
49 | * array of substitution values | ||
50 | */ | ||
51 | LeftInheritanceTuple(Tuple ancestor, Object[] localElements) { | ||
52 | super(ancestor); | ||
53 | this.localElements = localElements; | ||
54 | calcHash(); | ||
55 | } | ||
56 | |||
57 | // | ||
58 | // /** | ||
59 | // * Creates a Tuple instance of size one, fills it with the given object. | ||
60 | // * @pre: o!=null | ||
61 | // * @param o the single substitution | ||
62 | // */ | ||
63 | // public Tuple(Object o) | ||
64 | // { | ||
65 | // localElements = new Object [1]; | ||
66 | // localElements[0] = o; | ||
67 | // this.ancestor=null; | ||
68 | // this.inheritedIndex = 0; | ||
69 | // calcHash(); | ||
70 | // } | ||
71 | // | ||
72 | // /** | ||
73 | // * Creates a Tuple instance of size two, fills it with the given objects. | ||
74 | // * @pre: o1!=null, o2!=null | ||
75 | // */ | ||
76 | // public Tuple(Object o1, Object o2) | ||
77 | // { | ||
78 | // localElements = new Object [2]; | ||
79 | // localElements[0] = o1; | ||
80 | // localElements[1] = o2; | ||
81 | // this.ancestor=null; | ||
82 | // this.inheritedIndex = 0; | ||
83 | // calcHash(); | ||
84 | // } | ||
85 | // | ||
86 | // /** | ||
87 | // * Creates a Tuple instance of size three, fills it with the given | ||
88 | // objects. | ||
89 | // * @pre: o1!=null, o2!=null, o3!=null | ||
90 | // */ | ||
91 | // public Tuple(Object o1, Object o2, Object o3) | ||
92 | // { | ||
93 | // localElements = new Object [3]; | ||
94 | // localElements[0] = o1; | ||
95 | // localElements[1] = o2; | ||
96 | // localElements[2] = o3; | ||
97 | // this.ancestor=null; | ||
98 | // this.inheritedIndex = 0; | ||
99 | // calcHash(); | ||
100 | // } | ||
101 | |||
102 | /** | ||
103 | * @return number of elements | ||
104 | */ | ||
105 | public int getSize() { | ||
106 | return inheritedIndex + localElements.length; | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public int getLocalSize() { | ||
111 | return localElements.length; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * @pre: 0 <= index < getSize() | ||
116 | * | ||
117 | * @return the element at the specified index | ||
118 | */ | ||
119 | public Object get(int index) { | ||
120 | return (index < inheritedIndex) ? ancestor.get(index) : localElements[index - inheritedIndex]; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Optimized hash calculation | ||
125 | */ | ||
126 | @Override | ||
127 | void calcHash() { | ||
128 | final int PRIME = 31; | ||
129 | cachedHash = ancestor.hashCode(); | ||
130 | for (int i = 0; i < localElements.length; i++) { | ||
131 | cachedHash = PRIME * cachedHash; | ||
132 | Object element = localElements[i]; | ||
133 | if (element != null) | ||
134 | cachedHash += element.hashCode(); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Optimized equals calculation (prediction: true, since hash values match) | ||
140 | */ | ||
141 | @Override | ||
142 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
143 | if (other instanceof LeftInheritanceTuple) { | ||
144 | LeftInheritanceTuple lit = (LeftInheritanceTuple)other; | ||
145 | return Arrays.equals(this.localElements, lit.localElements); | ||
146 | } else { | ||
147 | if (localElements.length != other.getLocalSize()) | ||
148 | return false; | ||
149 | int index = inheritedIndex; | ||
150 | for (int i = 0; i<localElements.length; ++i) { | ||
151 | if (! Objects.equals(localElements[i], other.get(index++))) | ||
152 | return false; | ||
153 | } | ||
154 | return true; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | // public int compareTo(Object arg0) { | ||
159 | // Tuple other = (Tuple) arg0; | ||
160 | // | ||
161 | // int retVal = cachedHash - other.cachedHash; | ||
162 | // if (retVal==0) retVal = elements.length - other.elements.length; | ||
163 | // for (int i=0; retVal==0 && i<elements.length; ++i) | ||
164 | // { | ||
165 | // if (elements[i] == null && other.elements[i] != null) retVal = -1; | ||
166 | // else if (other.elements[i] == null) retVal = 1; | ||
167 | // else retVal = elements[i].compareTo(other.elements[i]); | ||
168 | // } | ||
169 | // return retVal; | ||
170 | // } | ||
171 | |||
172 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java new file mode 100644 index 00000000..61123176 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple1 extends BaseLeftInheritanceTuple { | ||
18 | /** | ||
19 | * A single substituted value after inheritedIndex. | ||
20 | */ | ||
21 | private final Object localElement; | ||
22 | |||
23 | /** | ||
24 | * @param ancestor | ||
25 | * @param localElement | ||
26 | */ | ||
27 | protected LeftInheritanceTuple1(Tuple ancestor, Object localElement) { | ||
28 | super(ancestor); | ||
29 | this.localElement = localElement; | ||
30 | calcHash(); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * @return number of elements | ||
35 | */ | ||
36 | public int getSize() { | ||
37 | return inheritedIndex + 1; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int getLocalSize() { | ||
42 | return 1; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * @pre: 0 <= index < getSize() | ||
47 | * | ||
48 | * @return the element at the specified index | ||
49 | */ | ||
50 | public Object get(int index) { | ||
51 | int local = index - inheritedIndex; | ||
52 | if (local < 0) | ||
53 | return ancestor.get(index); | ||
54 | else if (local == 0) return localElement; | ||
55 | else throw raiseIndexingError(index); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Optimized hash calculation | ||
60 | */ | ||
61 | @Override | ||
62 | void calcHash() { | ||
63 | final int PRIME = 31; | ||
64 | cachedHash = ancestor.hashCode(); | ||
65 | cachedHash = PRIME * cachedHash; | ||
66 | if (localElement != null) cachedHash += localElement.hashCode(); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Optimized equals calculation (prediction: true, since hash values match) | ||
71 | */ | ||
72 | @Override | ||
73 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
74 | if (other instanceof LeftInheritanceTuple1) { | ||
75 | LeftInheritanceTuple1 lit = (LeftInheritanceTuple1)other; | ||
76 | return Objects.equals(this.localElement, lit.localElement); | ||
77 | } else { | ||
78 | return (1 == other.getLocalSize()) && | ||
79 | Objects.equals(localElement, other.get(inheritedIndex)); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java new file mode 100644 index 00000000..e9fb98e8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple2 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | |||
21 | protected LeftInheritanceTuple2(Tuple ancestor, Object localElement0, Object localElement1) { | ||
22 | super(ancestor); | ||
23 | this.localElement0 = localElement0; | ||
24 | this.localElement1 = localElement1; | ||
25 | calcHash(); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getLocalSize() { | ||
30 | return 2; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int getSize() { | ||
35 | return inheritedIndex + 2; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Object get(int index) { | ||
40 | int local = index - inheritedIndex; | ||
41 | if (local < 0) | ||
42 | return ancestor.get(index); | ||
43 | else if (local == 0) return localElement0; | ||
44 | else if (local == 1) return localElement1; | ||
45 | else throw raiseIndexingError(index); | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Optimized hash calculation | ||
50 | */ | ||
51 | @Override | ||
52 | void calcHash() { | ||
53 | final int PRIME = 31; | ||
54 | cachedHash = ancestor.hashCode(); | ||
55 | cachedHash = PRIME * cachedHash; | ||
56 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
57 | cachedHash = PRIME * cachedHash; | ||
58 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
63 | if (other instanceof LeftInheritanceTuple2) { | ||
64 | LeftInheritanceTuple2 lit = (LeftInheritanceTuple2)other; | ||
65 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
66 | Objects.equals(this.localElement1, lit.localElement1); | ||
67 | } else { | ||
68 | return (2 == other.getLocalSize()) && | ||
69 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
70 | Objects.equals(localElement1, other.get(inheritedIndex + 1)); | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java new file mode 100644 index 00000000..e61d196f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple3 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | private final Object localElement2; | ||
21 | |||
22 | protected LeftInheritanceTuple3(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2) { | ||
23 | super(ancestor); | ||
24 | this.localElement0 = localElement0; | ||
25 | this.localElement1 = localElement1; | ||
26 | this.localElement2 = localElement2; | ||
27 | calcHash(); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public int getLocalSize() { | ||
32 | return 3; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public int getSize() { | ||
37 | return inheritedIndex + 3; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Object get(int index) { | ||
42 | int local = index - inheritedIndex; | ||
43 | if (local < 0) | ||
44 | return ancestor.get(index); | ||
45 | else if (local == 0) return localElement0; | ||
46 | else if (local == 1) return localElement1; | ||
47 | else if (local == 2) return localElement2; | ||
48 | else throw raiseIndexingError(index); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Optimized hash calculation | ||
53 | */ | ||
54 | @Override | ||
55 | void calcHash() { | ||
56 | final int PRIME = 31; | ||
57 | cachedHash = ancestor.hashCode(); | ||
58 | cachedHash = PRIME * cachedHash; | ||
59 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
60 | cachedHash = PRIME * cachedHash; | ||
61 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
62 | cachedHash = PRIME * cachedHash; | ||
63 | if (localElement2 != null) cachedHash += localElement2.hashCode(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
68 | if (other instanceof LeftInheritanceTuple3) { | ||
69 | LeftInheritanceTuple3 lit = (LeftInheritanceTuple3)other; | ||
70 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
71 | Objects.equals(this.localElement1, lit.localElement1) && | ||
72 | Objects.equals(this.localElement2, lit.localElement2); | ||
73 | } else { | ||
74 | return (3 == other.getLocalSize()) && | ||
75 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
76 | Objects.equals(localElement1, other.get(inheritedIndex + 1)) && | ||
77 | Objects.equals(localElement2, other.get(inheritedIndex + 2)); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java new file mode 100644 index 00000000..4e50f1e1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple4 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | private final Object localElement2; | ||
21 | private final Object localElement3; | ||
22 | |||
23 | protected LeftInheritanceTuple4(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2, | ||
24 | Object localElement3) { | ||
25 | super(ancestor); | ||
26 | this.localElement0 = localElement0; | ||
27 | this.localElement1 = localElement1; | ||
28 | this.localElement2 = localElement2; | ||
29 | this.localElement3 = localElement3; | ||
30 | calcHash(); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int getLocalSize() { | ||
35 | return 4; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int getSize() { | ||
40 | return inheritedIndex + 4; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Object get(int index) { | ||
45 | int local = index - inheritedIndex; | ||
46 | if (local < 0) | ||
47 | return ancestor.get(index); | ||
48 | else if (local == 0) return localElement0; | ||
49 | else if (local == 1) return localElement1; | ||
50 | else if (local == 2) return localElement2; | ||
51 | else if (local == 3) return localElement3; | ||
52 | else throw raiseIndexingError(index); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Optimized hash calculation | ||
57 | */ | ||
58 | @Override | ||
59 | void calcHash() { | ||
60 | final int PRIME = 31; | ||
61 | cachedHash = ancestor.hashCode(); | ||
62 | cachedHash = PRIME * cachedHash; | ||
63 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
64 | cachedHash = PRIME * cachedHash; | ||
65 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
66 | cachedHash = PRIME * cachedHash; | ||
67 | if (localElement2 != null) cachedHash += localElement2.hashCode(); | ||
68 | cachedHash = PRIME * cachedHash; | ||
69 | if (localElement3 != null) cachedHash += localElement3.hashCode(); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
74 | if (other instanceof LeftInheritanceTuple4) { | ||
75 | LeftInheritanceTuple4 lit = (LeftInheritanceTuple4)other; | ||
76 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
77 | Objects.equals(this.localElement1, lit.localElement1) && | ||
78 | Objects.equals(this.localElement2, lit.localElement2) && | ||
79 | Objects.equals(this.localElement3, lit.localElement3); | ||
80 | } else { | ||
81 | return (4 == other.getLocalSize()) && | ||
82 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
83 | Objects.equals(localElement1, other.get(inheritedIndex + 1)) && | ||
84 | Objects.equals(localElement2, other.get(inheritedIndex + 2)) && | ||
85 | Objects.equals(localElement3, other.get(inheritedIndex + 3)); | ||
86 | } | ||
87 | } | ||
88 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java new file mode 100644 index 00000000..a5d1991c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro | ||
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.tuple; | ||
11 | |||
12 | /** | ||
13 | * A tuple that transparently provides a masked (transformed) view of another tuple. | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 2.0 | ||
17 | * | ||
18 | */ | ||
19 | public class MaskedTuple extends Tuple { | ||
20 | |||
21 | Tuple wrapped; | ||
22 | TupleMask mask; | ||
23 | |||
24 | public MaskedTuple(Tuple wrapped, TupleMask mask) { | ||
25 | super(); | ||
26 | // if (wrapped instanceof MaskedTuple) { | ||
27 | // MaskedTuple parent = (MaskedTuple)wrapped; | ||
28 | // this.wrapped = parent.wrapped; | ||
29 | // this.mask = mask.transform(parent.mask); | ||
30 | // } | ||
31 | // else | ||
32 | //{ | ||
33 | this.wrapped = wrapped; | ||
34 | this.mask = mask; | ||
35 | //} | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Object get(int index) { | ||
40 | return wrapped.get(mask.indices[index]); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public int getSize() { | ||
45 | return mask.indices.length; | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java new file mode 100644 index 00000000..d94f545f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java | |||
@@ -0,0 +1,69 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.tuple; | ||
11 | |||
12 | /** | ||
13 | * Immutable tuple. Obtain instances via utility class {@link Tuples}. | ||
14 | * @author Gabor Bergmann | ||
15 | * | ||
16 | */ | ||
17 | public abstract class Tuple extends AbstractTuple { | ||
18 | |||
19 | /** | ||
20 | * Caches precalculated hash value | ||
21 | */ | ||
22 | protected int cachedHash; | ||
23 | |||
24 | /** | ||
25 | * Creates a Tuple instance Derivatives should call calcHash() | ||
26 | */ | ||
27 | protected Tuple() { | ||
28 | // calcHash(); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Hash calculation. Overrides should keep semantics. | ||
33 | */ | ||
34 | void calcHash() { | ||
35 | cachedHash = doCalcHash(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean equals(Object obj) { | ||
40 | if (this == obj) | ||
41 | return true; | ||
42 | if (obj instanceof ITuple) { | ||
43 | final ITuple other = (ITuple) obj; | ||
44 | return cachedHash == other.hashCode() && internalEquals(other); | ||
45 | } | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public int hashCode() { | ||
51 | // Calculated by #calcHash | ||
52 | return cachedHash; | ||
53 | } | ||
54 | |||
55 | public Tuple replaceAll(Object obsolete, Object replacement) { | ||
56 | Object[] oldElements = getElements(); | ||
57 | Object[] newElements = new Object[oldElements.length]; | ||
58 | for (int i = 0; i < oldElements.length; ++i) { | ||
59 | newElements[i] = obsolete.equals(oldElements[i]) ? replacement : oldElements[i]; | ||
60 | } | ||
61 | return Tuples.flatTupleOf(newElements); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public Tuple toImmutable() { | ||
66 | return this; | ||
67 | } | ||
68 | |||
69 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java new file mode 100644 index 00000000..49c55fef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java | |||
@@ -0,0 +1,560 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.tuple; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.OptionalInt; | ||
19 | import java.util.Set; | ||
20 | |||
21 | /** | ||
22 | * | ||
23 | * Specifies select indices of a tuple. If viewed through this mask (see {@link #transform(ITuple)}), the signature of the pattern will consist of its | ||
24 | * individual substitutions at the given positions, in the exact same order as they appear in indices[]. | ||
25 | * | ||
26 | * @author Gabor Bergmann | ||
27 | */ | ||
28 | public class TupleMask { | ||
29 | /** | ||
30 | * indices[i] specifies the index of the substitution in the original tuple that occupies the i-th place in the | ||
31 | * masked signature. | ||
32 | */ | ||
33 | public final int[] indices; | ||
34 | /** | ||
35 | * the size of the tuple this mask is applied to | ||
36 | */ | ||
37 | public int sourceWidth; | ||
38 | /** | ||
39 | * indicesSorted is indices, sorted in ascending order. | ||
40 | * null by default, call {@link #ensureIndicesSorted()} before using | ||
41 | */ | ||
42 | int[] indicesSorted; | ||
43 | |||
44 | /** | ||
45 | * true if no index occurs twice; computed on demand by {@link #isNonrepeating()} | ||
46 | */ | ||
47 | Boolean isNonrepeating; | ||
48 | |||
49 | /** | ||
50 | * Creates a TupleMask instance with the given indices array | ||
51 | * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known. | ||
52 | * @since 2.0 | ||
53 | */ | ||
54 | protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) { | ||
55 | this.indices = indices; | ||
56 | this.sourceWidth = sourceWidth; | ||
57 | this.indicesSorted = indicesSorted; | ||
58 | this.isNonrepeating = isNonrepeating; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Creates a TupleMask instance that selects given positions. | ||
63 | * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. | ||
64 | * | ||
65 | * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known. | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | protected static TupleMask fromSelectedIndicesInternal( | ||
69 | int[] selectedIndices, int sourceArity, | ||
70 | int[] indicesSorted, Boolean isNonrepeating) | ||
71 | { | ||
72 | if (selectedIndices.length == 0) // is it nullary? | ||
73 | return new TupleMask0(sourceArity); | ||
74 | |||
75 | // is it identity? | ||
76 | boolean identity = sourceArity == selectedIndices.length; | ||
77 | if (identity) { | ||
78 | for (int k=0; k < sourceArity; ++k) { | ||
79 | if (selectedIndices[k] != k) { | ||
80 | identity = false; | ||
81 | break; | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | if (identity) | ||
86 | return new TupleMaskIdentity(selectedIndices, sourceArity); | ||
87 | |||
88 | // generic case | ||
89 | return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Creates a TupleMask instance that selects given positions in monotonically increasing order. | ||
94 | * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. | ||
95 | * @since 2.0 | ||
96 | */ | ||
97 | protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity) | ||
98 | { | ||
99 | return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Creates a TupleMask instance of the given size that maps the first 'size' elements intact | ||
104 | */ | ||
105 | public static TupleMask linear(int size, int sourceWidth) { | ||
106 | if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth); | ||
107 | int[] indices = constructLinearSequence(size); | ||
108 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * An array containing the first {@link size} nonnegative integers in order | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | protected static int[] constructLinearSequence(int size) { | ||
116 | int[] indices = new int[size]; | ||
117 | for (int i = 0; i < size; i++) | ||
118 | indices[i] = i; | ||
119 | return indices; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Creates a TupleMask instance of the given size that maps every single element intact | ||
124 | */ | ||
125 | public static TupleMask identity(int size) { | ||
126 | return new TupleMaskIdentity(size); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * Creates a TupleMask instance of the given size that does not emit output. | ||
131 | */ | ||
132 | public static TupleMask empty(int sourceWidth) { | ||
133 | return linear(0, sourceWidth); | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is | ||
138 | * omitted | ||
139 | */ | ||
140 | public static TupleMask omit(int omission, int sourceWidth) { | ||
141 | int size = sourceWidth - 1; | ||
142 | int[] indices = new int[size]; | ||
143 | for (int i = 0; i < omission; i++) | ||
144 | indices[i] = i; | ||
145 | for (int i = omission; i < size; i++) | ||
146 | indices[i] = i + 1; | ||
147 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
148 | } | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Creates a TupleMask instance that selects positions where keep is true | ||
153 | * @since 1.7 | ||
154 | */ | ||
155 | public static TupleMask fromKeepIndicators(boolean[] keep) { | ||
156 | int size = 0; | ||
157 | for (int k = 0; k < keep.length; ++k) | ||
158 | if (keep[k]) | ||
159 | size++; | ||
160 | if (size == keep.length) return new TupleMaskIdentity(size); | ||
161 | int[] indices = new int[size]; | ||
162 | int l = 0; | ||
163 | for (int k = 0; k < keep.length; ++k) | ||
164 | if (keep[k]) | ||
165 | indices[l++] = k; | ||
166 | return fromSelectedMonotonicIndicesInternal(indices, keep.length); | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Creates a TupleMask instance that selects given positions. | ||
171 | * @since 1.7 | ||
172 | */ | ||
173 | public static TupleMask fromSelectedIndices(int sourceArity, Collection<Integer> selectedIndices) { | ||
174 | int[] selected = integersToIntArray(selectedIndices); | ||
175 | return fromSelectedIndicesInternal(selected, sourceArity, null, null); | ||
176 | } | ||
177 | /** | ||
178 | * Creates a TupleMask instance that selects given positions. | ||
179 | * @since 1.7 | ||
180 | */ | ||
181 | public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) { | ||
182 | return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null); | ||
183 | } | ||
184 | /** | ||
185 | * Creates a TupleMask instance that selects non-null positions of a given tuple | ||
186 | * @since 1.7 | ||
187 | */ | ||
188 | public static TupleMask fromNonNullIndices(ITuple tuple) { | ||
189 | List<Integer> indices = new ArrayList<>(); | ||
190 | for (int i=0; i < tuple.getSize(); i++) { | ||
191 | if (tuple.get(i) != null) { | ||
192 | indices.add(i); | ||
193 | } | ||
194 | } | ||
195 | if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size()); | ||
196 | return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize()); | ||
197 | } | ||
198 | /** | ||
199 | * @since 1.7 | ||
200 | */ | ||
201 | public static int[] integersToIntArray(Collection<Integer> selectedIndices) { | ||
202 | int[] selected = new int[selectedIndices.size()]; | ||
203 | int k=0; | ||
204 | for (Integer integer : selectedIndices) { | ||
205 | selected[k++] = integer; | ||
206 | } | ||
207 | return selected; | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary. | ||
213 | */ | ||
214 | public static TupleMask displace(int from, int to, int sourceWidth) { | ||
215 | if (from == to) return new TupleMaskIdentity(sourceWidth); | ||
216 | int[] indices = new int[sourceWidth]; | ||
217 | for (int i = 0; i < sourceWidth; i++) | ||
218 | if (i == to) | ||
219 | indices[i] = from; | ||
220 | else if (i >= from && i < to) | ||
221 | indices[i] = i + 1; | ||
222 | else if (i > to && i <= from) | ||
223 | indices[i] = i - 1; | ||
224 | else | ||
225 | indices[i] = i; | ||
226 | return fromSelectedIndicesInternal(indices, sourceWidth, null, true); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Creates a TupleMask instance that selects a single element of the tuple. | ||
231 | */ | ||
232 | public static TupleMask selectSingle(int selected, int sourceWidth) { | ||
233 | int[] indices = { selected }; | ||
234 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by | ||
239 | * right. PRE: left and right have the same sourcewidth | ||
240 | */ | ||
241 | public static TupleMask append(TupleMask left, TupleMask right) { | ||
242 | int leftLength = left.indices.length; | ||
243 | int rightLength = right.indices.length; | ||
244 | int[] indices = new int[leftLength + rightLength]; | ||
245 | for (int i = 0; i < leftLength; ++i) | ||
246 | indices[i] = left.indices[i]; | ||
247 | for (int i = 0; i < rightLength; ++i) | ||
248 | indices[i + leftLength] = right.indices[i]; | ||
249 | return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null); | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * Generates indicesSorted from indices on demand | ||
254 | */ | ||
255 | void ensureIndicesSorted() { | ||
256 | if (indicesSorted == null) { | ||
257 | indicesSorted = new int[indices.length]; | ||
258 | List<Integer> list = new LinkedList<Integer>(); | ||
259 | for (int i = 0; i < indices.length; ++i) | ||
260 | list.add(indices[i]); | ||
261 | java.util.Collections.sort(list); | ||
262 | int i = 0; | ||
263 | for (Integer a : list) | ||
264 | indicesSorted[i++] = a; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | |||
269 | |||
270 | /** | ||
271 | * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected. | ||
272 | * <p> PRE: mask indices are all different | ||
273 | * @since 2.0 | ||
274 | */ | ||
275 | public OptionalInt getFirstOmittedIndex() { | ||
276 | ensureIndicesSorted(); | ||
277 | int column = 0; | ||
278 | while (column < getSize() && indicesSorted[column] == column) column++; | ||
279 | if (column < getSourceWidth()) return OptionalInt.of(column); | ||
280 | else return OptionalInt.empty(); | ||
281 | } | ||
282 | |||
283 | |||
284 | /** | ||
285 | * Returns a selected masked value from the selected tuple. | ||
286 | * @pre: 0 <= index < getSize() | ||
287 | * @since 1.7 | ||
288 | */ | ||
289 | public Object getValue(ITuple original, int index) { | ||
290 | return original.get(indices[index]); | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * Sets the selected value in the original tuple based on the mask definition | ||
295 | * | ||
296 | * @pre: 0 <= index < getSize() | ||
297 | * @since 1.7 | ||
298 | */ | ||
299 | public void set(IModifiableTuple tuple, int index, Object value) { | ||
300 | tuple.set(indices[index], value); | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * Generates an immutable, masked view of the original tuple. | ||
305 | * <p> The new tuple will have arity {@link #getSize()}, | ||
306 | * and will consist of the elements of the original tuple, at positions indicated by this mask. | ||
307 | * @since 1.7 | ||
308 | */ | ||
309 | public Tuple transform(ITuple original) { | ||
310 | switch (indices.length) { | ||
311 | case 0: | ||
312 | return FlatTuple0.INSTANCE; | ||
313 | case 1: | ||
314 | return new FlatTuple1(original.get(indices[0])); | ||
315 | case 2: | ||
316 | return new FlatTuple2(original.get(indices[0]), original.get(indices[1])); | ||
317 | case 3: | ||
318 | return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2])); | ||
319 | case 4: | ||
320 | return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3])); | ||
321 | default: | ||
322 | Object signature[] = new Object[indices.length]; | ||
323 | for (int i = 0; i < indices.length; ++i) | ||
324 | signature[i] = original.get(indices[i]); | ||
325 | return new FlatTuple(signature); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * @return true iff no two selected indices are the same | ||
331 | * @since 2.0 | ||
332 | */ | ||
333 | public boolean isNonrepeating() { | ||
334 | if (isNonrepeating == null) { | ||
335 | ensureIndicesSorted(); | ||
336 | int previous = -1; | ||
337 | int i; | ||
338 | for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) { | ||
339 | previous = indicesSorted[i]; | ||
340 | } | ||
341 | isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition | ||
342 | } | ||
343 | return isNonrepeating; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple | ||
348 | * that are not determined this way will be filled with null. | ||
349 | * | ||
350 | * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true | ||
351 | * @since 1.7 | ||
352 | */ | ||
353 | public Tuple revertFrom(ITuple masked) { | ||
354 | Object[] signature = new Object[sourceWidth]; | ||
355 | for (int i = 0; i < indices.length; ++i) | ||
356 | signature[indices[i]] = masked.get(i); | ||
357 | return Tuples.flatTupleOf(signature); | ||
358 | } | ||
359 | |||
360 | /** | ||
361 | * Returns a tuple `result`, same arity as the original tuple, that satisfies | ||
362 | * `this.transform(result).equals(this.transform(tuple))`. | ||
363 | * Positions of the result tuple that are not determined this way will be filled with null. | ||
364 | * <p> In other words, a copy of the original tuple is returned, | ||
365 | * with null substituted at each position that is <em>not</em> selected by this mask. | ||
366 | * | ||
367 | * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true | ||
368 | * @since 2.1 | ||
369 | */ | ||
370 | public Tuple keepSelectedIndices(ITuple original) { | ||
371 | Object[] signature = new Object[sourceWidth]; | ||
372 | for (int i = 0; i < indices.length; ++i) | ||
373 | signature[indices[i]] = original.get(indices[i]); | ||
374 | return Tuples.flatTupleOf(signature); | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * Generates an immutable, masked view of the original tuple. | ||
379 | * <p> The list will have arity {@link #getSize()}, | ||
380 | * and will consist of the elements of the original tuple, at positions indicated by this mask. | ||
381 | */ | ||
382 | public <T> List<T> transform(List<T> original) { | ||
383 | List<T> signature = new ArrayList<T>(indices.length); | ||
384 | for (int i = 0; i < indices.length; ++i) | ||
385 | signature.add(original.get(indices[i])); | ||
386 | return signature; | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask. | ||
391 | * | ||
392 | * @return a mask that cascades the effects this mask after the mask provided as parameter. | ||
393 | */ | ||
394 | public TupleMask transform(TupleMask mask) { | ||
395 | int[] cascadeIndices = new int[indices.length]; | ||
396 | for (int i = 0; i < indices.length; ++i) | ||
397 | cascadeIndices[i] = mask.indices[indices[i]]; | ||
398 | return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null); | ||
399 | } | ||
400 | |||
401 | // /** | ||
402 | // * Generates a complementer mask that maps those elements that were | ||
403 | // untouched by the original mask. | ||
404 | // * Ordering is left intact. | ||
405 | // * A Tuple is used for reference concerning possible equalities among | ||
406 | // elements. | ||
407 | // */ | ||
408 | // public TupleMask complementer(Tuple reference) | ||
409 | // { | ||
410 | // HashSet<Object> touched = new HashSet<Object>(); | ||
411 | // LinkedList<Integer> untouched = new LinkedList<Integer>(); | ||
412 | // | ||
413 | // for (int index : indices) touched.add(reference.get(index)); | ||
414 | // for (int index=0; index<reference.getSize(); ++index) | ||
415 | // { | ||
416 | // if (touched.add(reference.get(index))) untouched.addLast(index); | ||
417 | // } | ||
418 | // | ||
419 | // int[] complementer = new int[untouched.size()]; | ||
420 | // int k = 0; | ||
421 | // for (Integer integer : untouched) complementer[k++] = integer; | ||
422 | // return new TupleMask(complementer, reference.getSize()); | ||
423 | // } | ||
424 | |||
425 | /** | ||
426 | * Combines two substitutions. The new pattern will contain all substitutions of masked and unmasked, assuming that | ||
427 | * the elements of masked indicated by this mask are already matched against unmasked. | ||
428 | * | ||
429 | * POST: the result will start with an exact copy of unmasked | ||
430 | * | ||
431 | * @param unmasked | ||
432 | * primary pattern substitution that is left intact. | ||
433 | * @param masked | ||
434 | * secondary pattern substitution that is transformed to the end of the result. | ||
435 | * @param useInheritance | ||
436 | * whether to use inheritance or copy umasked into result instead. | ||
437 | * @param asComplementer | ||
438 | * whether this mask maps from the masked Tuple to the tail of the result or to the unmasked one. | ||
439 | * @return new pattern that is a combination of unmasked and masked. | ||
440 | */ | ||
441 | public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { | ||
442 | |||
443 | int combinedLength = asComplementer ? indices.length : masked.getSize() - indices.length; | ||
444 | if (!useInheritance) | ||
445 | combinedLength += unmasked.getSize(); | ||
446 | Object combined[] = new Object[combinedLength]; | ||
447 | |||
448 | int cPos = 0; | ||
449 | if (!useInheritance) { | ||
450 | for (int i = 0; i < unmasked.getSize(); ++i) | ||
451 | combined[cPos++] = unmasked.get(i); | ||
452 | } | ||
453 | |||
454 | if (asComplementer) { | ||
455 | for (int i = 0; i < indices.length; ++i) | ||
456 | combined[cPos++] = masked.get(indices[i]); | ||
457 | } else { | ||
458 | ensureIndicesSorted(); | ||
459 | int mPos = 0; | ||
460 | for (int i = 0; i < masked.getSize(); ++i) | ||
461 | if (mPos < indicesSorted.length && i == indicesSorted[mPos]) | ||
462 | mPos++; | ||
463 | else | ||
464 | combined[cPos++] = masked.get(i); | ||
465 | } | ||
466 | |||
467 | return useInheritance ? Tuples.leftInheritanceTupleOf(unmasked, combined) : Tuples.flatTupleOf(combined); | ||
468 | } | ||
469 | |||
470 | @Override | ||
471 | public int hashCode() { | ||
472 | final int PRIME = 31; | ||
473 | int result = sourceWidth; | ||
474 | for (int i : indices) | ||
475 | result = PRIME * result + i; | ||
476 | return result; | ||
477 | } | ||
478 | |||
479 | @Override | ||
480 | public boolean equals(Object obj) { | ||
481 | if (this == obj) | ||
482 | return true; | ||
483 | if (obj == null) | ||
484 | return false; | ||
485 | if (getClass() != obj.getClass()) | ||
486 | return false; | ||
487 | final TupleMask other = (TupleMask) obj; | ||
488 | if (sourceWidth != other.sourceWidth) | ||
489 | return false; | ||
490 | if (indices.length != other.indices.length) | ||
491 | return false; | ||
492 | for (int k = 0; k < indices.length; k++) | ||
493 | if (indices[k] != other.indices[k]) | ||
494 | return false; | ||
495 | return true; | ||
496 | } | ||
497 | |||
498 | @Override | ||
499 | public String toString() { | ||
500 | StringBuilder s = new StringBuilder(); | ||
501 | s.append("M(" + sourceWidth + "->"); | ||
502 | for (int i : indices) { | ||
503 | s.append(i); | ||
504 | s.append(','); | ||
505 | } | ||
506 | s.append(')'); | ||
507 | return s.toString(); | ||
508 | } | ||
509 | |||
510 | /** | ||
511 | * Returns the size of the masked tuples described by this mask | ||
512 | * @since 1.7 | ||
513 | */ | ||
514 | public int getSize() { | ||
515 | return indices.length; | ||
516 | } | ||
517 | |||
518 | /** | ||
519 | * Returns the size of the original tuples handled by this mask | ||
520 | * @since 1.7 | ||
521 | */ | ||
522 | public int getSourceWidth() { | ||
523 | return sourceWidth; | ||
524 | } | ||
525 | |||
526 | |||
527 | /** | ||
528 | * @return true iff this mask is a no-op | ||
529 | * @since 2.0 | ||
530 | */ | ||
531 | public boolean isIdentity() { | ||
532 | // Contract: if identity mask, a specialized subclass is constructed instead | ||
533 | return false; | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element | ||
538 | * in case of the mapping result in duplicate values. | ||
539 | * | ||
540 | * @since 1.7 | ||
541 | */ | ||
542 | public <T> Set<T> transformUnique(List<T> original) { | ||
543 | Set<T> signature = new HashSet<>(); | ||
544 | for (int i = 0; i < indices.length; ++i) | ||
545 | signature.add(original.get(indices[i])); | ||
546 | return signature; | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * @return the list of selected indices | ||
551 | * @since 2.1 | ||
552 | */ | ||
553 | public List<Integer> getIndicesAsList() { | ||
554 | List<Integer> result = new ArrayList<Integer>(indices.length); | ||
555 | for (int i = 0; i < indices.length; ++i) | ||
556 | result.add(indices[i]); | ||
557 | return result; | ||
558 | } | ||
559 | |||
560 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java new file mode 100644 index 00000000..5a0c79ff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java | |||
@@ -0,0 +1,56 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public final class TupleMask0 extends TupleMask { | ||
19 | |||
20 | private final static int[] EMPTY_ARRAY = {}; | ||
21 | |||
22 | /** | ||
23 | * PRE: indices.length == 0 | ||
24 | */ | ||
25 | TupleMask0(int sourceWidth) { | ||
26 | super(EMPTY_ARRAY, sourceWidth, EMPTY_ARRAY, true); | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public <T> List<T> transform(List<T> original) { | ||
31 | return Collections.emptyList(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Tuple transform(ITuple original) { | ||
36 | return Tuples.staticArityFlatTupleOf(); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public TupleMask transform(TupleMask mask) { | ||
41 | return new TupleMask0(mask.sourceWidth); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { | ||
46 | if (asComplementer) | ||
47 | return unmasked; | ||
48 | else | ||
49 | return super.combine(unmasked, masked, useInheritance, asComplementer); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean isIdentity() { | ||
54 | return 0 == sourceWidth; | ||
55 | } | ||
56 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java new file mode 100644 index 00000000..62746587 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class TupleMaskIdentity extends TupleMask { | ||
18 | |||
19 | TupleMaskIdentity(int sourceWidth) { | ||
20 | this(constructLinearSequence(sourceWidth), sourceWidth); | ||
21 | } | ||
22 | TupleMaskIdentity(int[] indices, int sourceWidth) { | ||
23 | super(indices, sourceWidth, indices, true); | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public <T> List<T> transform(List<T> original) { | ||
28 | return original; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public Tuple transform(ITuple original) { | ||
33 | return original.toImmutable(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public TupleMask transform(TupleMask mask) { | ||
38 | return mask; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public Tuple revertFrom(ITuple masked) { | ||
43 | return masked.toImmutable(); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public boolean isIdentity() { | ||
48 | return true; | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java new file mode 100644 index 00000000..79193516 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.tuple; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
14 | |||
15 | /** | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public class TupleValueProvider implements IValueProvider { | ||
20 | |||
21 | final ITuple tuple; | ||
22 | final Map<String, Integer> indexMapping; | ||
23 | |||
24 | /** | ||
25 | * Wraps a tuple with an index mapping | ||
26 | * @param tuple | ||
27 | * @param indexMapping | ||
28 | */ | ||
29 | public TupleValueProvider(ITuple tuple, Map<String, Integer> indexMapping) { | ||
30 | super(); | ||
31 | this.tuple = tuple; | ||
32 | this.indexMapping = indexMapping; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Object getValue(String variableName) { | ||
37 | Integer index = indexMapping.get(variableName); | ||
38 | if (index == null) { | ||
39 | throw new IllegalArgumentException(String.format("Variable %s is not present in mapping.", variableName)); | ||
40 | } | ||
41 | Object value = tuple.get(index); | ||
42 | if (value == null) { | ||
43 | throw new IllegalArgumentException(String.format("Variable %s is not found using index %d.", variableName, index)); | ||
44 | } | ||
45 | return value; | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java new file mode 100644 index 00000000..5e41d7d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java | |||
@@ -0,0 +1,157 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Common static factory utilities for tuples. | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public class Tuples { | ||
18 | |||
19 | private Tuples() { | ||
20 | // Empty utility class constructor | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Creates a flat tuple consisting of the given elements. | ||
25 | * For low-arity tuples, specialized implementations | ||
26 | * (such as {@link FlatTuple2}) will be instantiated. | ||
27 | * | ||
28 | * <p> In case the exact arity is <i>statically</i> known, | ||
29 | * it may be more efficient for the client to instantiate | ||
30 | * the appropriate specialized implementation | ||
31 | * (via {@link #staticArityFlatTupleOf(Object, Object)} etc. | ||
32 | * or {@link #wideFlatTupleOf(Object...)}), | ||
33 | * instead of invoking this method. | ||
34 | * This method does a runtime arity check, and therefore | ||
35 | * also appropriate if the arity is determined at runtime. | ||
36 | */ | ||
37 | public static Tuple flatTupleOf(Object... elements) { | ||
38 | switch (elements.length) { | ||
39 | case 0: | ||
40 | return FlatTuple0.INSTANCE; | ||
41 | case 1: | ||
42 | return new FlatTuple1(elements[0]); | ||
43 | case 2: | ||
44 | return new FlatTuple2(elements[0], elements[1]); | ||
45 | case 3: | ||
46 | return new FlatTuple3(elements[0], elements[1], elements[2]); | ||
47 | case 4: | ||
48 | return new FlatTuple4(elements[0], elements[1], elements[2], elements[3]); | ||
49 | default: | ||
50 | return new FlatTuple(elements); | ||
51 | } | ||
52 | } | ||
53 | /** | ||
54 | * Creates a left inheritance tuple that extends an ancestor tuple | ||
55 | * by the given "local" elements. | ||
56 | * For locally low-arity tuples, specialized implementations | ||
57 | * (such as {@link LeftInheritanceTuple2}) will be instantiated. | ||
58 | * | ||
59 | * <p> In case the exact arity is <i>statically</i> known, | ||
60 | * it may be more efficient for the client to instantiate | ||
61 | * the appropriate specialized implementation | ||
62 | * (via {@link #staticArityLeftInheritanceTupleOf(Object, Object)} etc. | ||
63 | * or {@link #wideLeftInheritanceTupleOf(Object...)}), | ||
64 | * instead of invoking this method. | ||
65 | * This method does a runtime arity check, and therefore | ||
66 | * also appropriate if the arity is determined at runtime. | ||
67 | */ | ||
68 | public static Tuple leftInheritanceTupleOf(Tuple ancestor, Object... localElements) { | ||
69 | switch (localElements.length) { | ||
70 | case 0: | ||
71 | return ancestor; | ||
72 | case 1: | ||
73 | return new LeftInheritanceTuple1(ancestor, localElements[0]); | ||
74 | case 2: | ||
75 | return new LeftInheritanceTuple2(ancestor, localElements[0], localElements[1]); | ||
76 | case 3: | ||
77 | return new LeftInheritanceTuple3(ancestor, localElements[0], localElements[1], localElements[2]); | ||
78 | case 4: | ||
79 | return new LeftInheritanceTuple4(ancestor, localElements[0], localElements[1], localElements[2], localElements[3]); | ||
80 | default: | ||
81 | return new LeftInheritanceTuple(ancestor, localElements); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Creates a flat tuple consisting of no elements. | ||
87 | */ | ||
88 | public static Tuple staticArityFlatTupleOf() { | ||
89 | return FlatTuple0.INSTANCE; | ||
90 | } | ||
91 | /** | ||
92 | * Creates a flat tuple consisting of the given single element. | ||
93 | */ | ||
94 | public static Tuple staticArityFlatTupleOf(Object element) { | ||
95 | return new FlatTuple1(element); | ||
96 | } | ||
97 | /** | ||
98 | * Creates a flat tuple consisting of the given elements. | ||
99 | */ | ||
100 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1) { | ||
101 | return new FlatTuple2(element0, element1); | ||
102 | } | ||
103 | /** | ||
104 | * Creates a flat tuple consisting of the given elements. | ||
105 | */ | ||
106 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2) { | ||
107 | return new FlatTuple3(element0, element1, element2); | ||
108 | } | ||
109 | /** | ||
110 | * Creates a flat tuple consisting of the given elements. | ||
111 | */ | ||
112 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2, Object element3) { | ||
113 | return new FlatTuple4(element0, element1, element2, element3); | ||
114 | } | ||
115 | /** | ||
116 | * Creates a flat tuple consisting of the given elements. | ||
117 | * <p> Invoke this only if it is statically known that the tuple will be wide. | ||
118 | * Otherwise, use {@link #flatTupleOf(Object...)}. | ||
119 | */ | ||
120 | public static Tuple wideFlatTupleOf(Object... elements) { | ||
121 | return new FlatTuple(elements); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Creates a left inheritance tuple consisting of the given single local element. | ||
126 | */ | ||
127 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element) { | ||
128 | return new LeftInheritanceTuple1(ancestor, element); | ||
129 | } | ||
130 | /** | ||
131 | * Creates a left inheritance tuple consisting of the given local elements. | ||
132 | */ | ||
133 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1) { | ||
134 | return new LeftInheritanceTuple2(ancestor, element0, element1); | ||
135 | } | ||
136 | /** | ||
137 | * Creates a left inheritance tuple consisting of the given local elements. | ||
138 | */ | ||
139 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2) { | ||
140 | return new LeftInheritanceTuple3(ancestor, element0, element1, element2); | ||
141 | } | ||
142 | /** | ||
143 | * Creates a left inheritance tuple consisting of the given local elements. | ||
144 | */ | ||
145 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2, Object element3) { | ||
146 | return new LeftInheritanceTuple4(ancestor, element0, element1, element2, element3); | ||
147 | } | ||
148 | /** | ||
149 | * Creates a left inheritance tuple consisting of the given local elements. | ||
150 | * <p> Invoke this only if it is statically known that the tuple will be wide. | ||
151 | * Otherwise, use {@link #leftInheritanceTupleOf(Tuple, Object...)}. | ||
152 | */ | ||
153 | public static Tuple wideLeftInheritanceTupleOf(Tuple ancestor, Object... elements) { | ||
154 | return new LeftInheritanceTuple(ancestor, elements); | ||
155 | } | ||
156 | |||
157 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java new file mode 100644 index 00000000..f683d544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.tuple; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
12 | |||
13 | /** | ||
14 | * This class provides a volatile tuple view with a given mask of a given tuple instance. If the masked tuple changes, | ||
15 | * the view updates as well. | ||
16 | * | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 1.7 | ||
19 | * | ||
20 | */ | ||
21 | public class VolatileMaskedTuple extends VolatileTuple { | ||
22 | |||
23 | protected final TupleMask mask; | ||
24 | protected ITuple source; | ||
25 | |||
26 | public VolatileMaskedTuple(ITuple source, TupleMask mask) { | ||
27 | this.source = source; | ||
28 | this.mask = mask; | ||
29 | } | ||
30 | |||
31 | public VolatileMaskedTuple(TupleMask mask) { | ||
32 | this(null, mask); | ||
33 | } | ||
34 | |||
35 | public void updateTuple(ITuple newSource) { | ||
36 | source = newSource; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Object get(int index) { | ||
41 | Preconditions.checkState(source != null, "Source tuple is not set."); | ||
42 | return mask.getValue(source, index); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getSize() { | ||
47 | return mask.getSize(); | ||
48 | } | ||
49 | |||
50 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java new file mode 100644 index 00000000..92306c6e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.tuple; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
12 | |||
13 | /** | ||
14 | * A masked tuple implementation that allows modifying the backing tuple. | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * @since 1.7 | ||
17 | * | ||
18 | */ | ||
19 | public class VolatileModifiableMaskedTuple extends VolatileMaskedTuple implements IModifiableTuple { | ||
20 | |||
21 | private IModifiableTuple modifiableTuple; | ||
22 | |||
23 | public VolatileModifiableMaskedTuple(IModifiableTuple source, TupleMask mask) { | ||
24 | super(source, mask); | ||
25 | modifiableTuple = source; | ||
26 | } | ||
27 | |||
28 | public VolatileModifiableMaskedTuple(TupleMask mask) { | ||
29 | this(null, mask); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void updateTuple(ITuple newSource) { | ||
34 | Preconditions.checkArgument(newSource instanceof IModifiableTuple, "Provided tuple does not support updates"); | ||
35 | this.updateTuple((IModifiableTuple)newSource); | ||
36 | } | ||
37 | |||
38 | public void updateTuple(IModifiableTuple newSource) { | ||
39 | super.updateTuple(newSource); | ||
40 | modifiableTuple = newSource; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void set(int index, Object value) { | ||
45 | mask.set(modifiableTuple, index, value); | ||
46 | } | ||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java new file mode 100644 index 00000000..699105a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017 Zoltan Ujhelyi, IncQuery Labs | ||
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.tuple; | ||
11 | |||
12 | /** | ||
13 | * Mutable tuple without explicit modification commands. In practical terms, the values stored in a volatile tuple can | ||
14 | * be changed without any notification. | ||
15 | * | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public abstract class VolatileTuple extends AbstractTuple { | ||
21 | |||
22 | @Override | ||
23 | public boolean equals(Object obj) { | ||
24 | if (this == obj) | ||
25 | return true; | ||
26 | if (obj == null) | ||
27 | return false; | ||
28 | if (!(obj instanceof ITuple)) | ||
29 | return false; | ||
30 | final ITuple other = (ITuple) obj; | ||
31 | return internalEquals(other); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int hashCode() { | ||
36 | return doCalcHash(); | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Creates an immutable tuple from the values stored in the tuple. The created tuple will not be updated when the | ||
41 | * current tuple changes. | ||
42 | */ | ||
43 | @Override | ||
44 | public Tuple toImmutable() { | ||
45 | return Tuples.flatTupleOf(getElements()); | ||
46 | } | ||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java new file mode 100644 index 00000000..338990ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java | |||
@@ -0,0 +1,48 @@ | |||
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.util; | ||
10 | |||
11 | /** | ||
12 | * The degree of accuracy of a cardinality estimate | ||
13 | * @author Gabor Bergmann | ||
14 | * @since 2.1 | ||
15 | */ | ||
16 | public enum Accuracy { | ||
17 | EXACT_COUNT, | ||
18 | BEST_UPPER_BOUND, | ||
19 | BEST_LOWER_BOUND, | ||
20 | APPROXIMATION; | ||
21 | |||
22 | /** | ||
23 | * Partial order comparison. | ||
24 | */ | ||
25 | public boolean atLeastAsPreciseAs(Accuracy other) { | ||
26 | switch (this) { | ||
27 | case EXACT_COUNT: return true; | ||
28 | case APPROXIMATION: return APPROXIMATION == other; | ||
29 | case BEST_UPPER_BOUND: return BEST_UPPER_BOUND == other || APPROXIMATION == other; | ||
30 | case BEST_LOWER_BOUND: return BEST_LOWER_BOUND == other || APPROXIMATION == other; | ||
31 | default: throw new IllegalArgumentException(); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @return another accuracy value that is anti-monotonic to this one, | ||
37 | * i.e. an accuracy that should be used in the denominator to obtain a fraction with this accuracy | ||
38 | */ | ||
39 | public Accuracy reciprocal() { | ||
40 | switch(this) { | ||
41 | case APPROXIMATION: return APPROXIMATION; | ||
42 | case BEST_UPPER_BOUND: return BEST_LOWER_BOUND; | ||
43 | case BEST_LOWER_BOUND: return BEST_UPPER_BOUND; | ||
44 | case EXACT_COUNT: return EXACT_COUNT; | ||
45 | default: throw new IllegalArgumentException(); | ||
46 | } | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java new file mode 100644 index 00000000..1b09aec6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.util; | ||
11 | |||
12 | /** | ||
13 | * @author Gabor Bergmann | ||
14 | * @since 1.7 | ||
15 | * An instance of clearable pattern memory. | ||
16 | */ | ||
17 | public interface Clearable { | ||
18 | /** | ||
19 | * Clear all partial matchings stored in memory | ||
20 | * | ||
21 | */ | ||
22 | void clear(); | ||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java new file mode 100644 index 00000000..590a1ec3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java | |||
@@ -0,0 +1,188 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Istvan Rath and Daniel Varro | ||
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.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.TreeMap; | ||
16 | import java.util.function.Function; | ||
17 | |||
18 | /** | ||
19 | * Factory class used as an accessor to Collections implementations. | ||
20 | * @author istvanrath | ||
21 | */ | ||
22 | public final class CollectionsFactory | ||
23 | { | ||
24 | |||
25 | /** | ||
26 | * Instantiates a new empty map. | ||
27 | * @since 1.7 | ||
28 | */ | ||
29 | public static <K, V> Map<K, V> createMap() { | ||
30 | return FRAMEWORK.createMap(); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Instantiates a new map with the given initial contents. | ||
35 | * @since 1.7 | ||
36 | */ | ||
37 | public static <K, V> Map<K, V> createMap(Map<K, V> initial) { | ||
38 | return FRAMEWORK.createMap(initial); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Instantiates a new tree map. | ||
43 | * @since 2.3 | ||
44 | */ | ||
45 | public static <K, V> TreeMap<K, V> createTreeMap() { | ||
46 | return FRAMEWORK.createTreeMap(); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * Instantiates a new empty set. | ||
51 | * @since 1.7 | ||
52 | */ | ||
53 | public static <E> Set<E> createSet() { | ||
54 | return FRAMEWORK.createSet(); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Instantiates a new set with the given initial contents. | ||
59 | * @since 1.7 | ||
60 | */ | ||
61 | public static <E> Set<E> createSet(Collection<E> initial) { | ||
62 | return FRAMEWORK.createSet(initial); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Instantiates an empty set; the key parameter is used to allow using this as a method reference as a | ||
67 | * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. | ||
68 | * | ||
69 | * @param key | ||
70 | * the value of this parameter is ignored | ||
71 | * @since 2.0 | ||
72 | */ | ||
73 | public static <T> Set<T> emptySet(Object key) { | ||
74 | return FRAMEWORK.createSet(); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Instantiates a new empty multiset. | ||
79 | * @since 1.7 | ||
80 | */ | ||
81 | public static <T> IMultiset<T> createMultiset() { | ||
82 | return FRAMEWORK.createMultiset(); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Instantiates an empty multiset; the key parameter is used to allow using this as a method reference as a | ||
87 | * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. | ||
88 | * | ||
89 | * @param key | ||
90 | * the value of this parameter is ignored | ||
91 | * @since 2.0 | ||
92 | */ | ||
93 | public static <T> IMultiset<T> emptyMultiset(Object key) { | ||
94 | return FRAMEWORK.createMultiset(); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Instantiates a new empty delta bag. | ||
99 | * @since 1.7 | ||
100 | */ | ||
101 | public static <T> IDeltaBag<T> createDeltaBag() { | ||
102 | return FRAMEWORK.createDeltaBag(); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Instantiates a new list that is optimized for registering observers / callbacks. | ||
107 | * @since 1.7 | ||
108 | */ | ||
109 | public static <O> List<O> createObserverList() { | ||
110 | return FRAMEWORK.createObserverList(); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Instantiates a size-optimized multimap from keys to sets of values. | ||
115 | * <p>For a single key, many values can be associated according to the given bucket semantics. | ||
116 | * <p>The keys and values are stored as type fromKeys resp. ofValues; | ||
117 | * currently Object.class and Long.class are supported. | ||
118 | * @since 2.0 | ||
119 | */ | ||
120 | public static <K, V> IMultiLookup<K, V> createMultiLookup( | ||
121 | Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues) { | ||
122 | return FRAMEWORK.createMultiLookup(fromKeys, toBuckets, ofValues); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Instantiates a memory storing values. | ||
127 | * <p>For a single key, many values can be associated according to the given memory semantics. | ||
128 | * <p>The values are stored as type 'values'; | ||
129 | * currently Object.class and Long.class are supported. | ||
130 | * @since 2.0 | ||
131 | */ | ||
132 | public static <T> IMemory<T> createMemory( | ||
133 | Class<? super T> values, MemoryType memoryType) { | ||
134 | return FRAMEWORK.createMemory(values, memoryType); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * The type of {@link IMemory} | ||
139 | * @since 2.0 | ||
140 | * TODO add delta as type | ||
141 | */ | ||
142 | public enum MemoryType { | ||
143 | /** | ||
144 | * A single key-value pair is stored at most once | ||
145 | */ | ||
146 | SETS, | ||
147 | /** | ||
148 | * Duplicate key-value pairs allowed | ||
149 | */ | ||
150 | MULTISETS | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * The collections framework of the current configuration. | ||
155 | * @since 1.7 | ||
156 | */ | ||
157 | private static final ICollectionsFramework FRAMEWORK = new EclipseCollectionsFactory(); | ||
158 | |||
159 | /** | ||
160 | * Interface abstracting over a collections technology that provides custom collection implementations. | ||
161 | * @since 1.7 | ||
162 | */ | ||
163 | public static interface ICollectionsFramework { | ||
164 | |||
165 | public abstract <K,V> Map<K,V> createMap(); | ||
166 | public abstract <K,V> Map<K,V> createMap(Map<K,V> initial); | ||
167 | /** | ||
168 | * @since 2.3 | ||
169 | */ | ||
170 | public abstract <K, V> TreeMap<K, V> createTreeMap(); | ||
171 | public abstract <E> Set<E> createSet(); | ||
172 | public abstract <E> Set<E> createSet(Collection<E> initial); | ||
173 | public abstract <T> IMultiset<T> createMultiset(); | ||
174 | public abstract <T> IDeltaBag<T> createDeltaBag(); | ||
175 | public abstract <O> List<O> createObserverList(); | ||
176 | |||
177 | /** | ||
178 | * @since 2.0 | ||
179 | */ | ||
180 | public abstract <K, V> IMultiLookup<K, V> createMultiLookup( | ||
181 | Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues); | ||
182 | /** | ||
183 | * @since 2.0 | ||
184 | */ | ||
185 | public abstract <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType); | ||
186 | } | ||
187 | |||
188 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java new file mode 100644 index 00000000..88f7ec00 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util; | ||
10 | |||
11 | /** | ||
12 | * Indicates whether a propagated update event signals the insertion or deletion of an element | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | */ | ||
16 | public enum Direction { | ||
17 | INSERT, DELETE; | ||
18 | |||
19 | /** | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public Direction opposite() { | ||
23 | switch (this) { | ||
24 | case INSERT: | ||
25 | return DELETE; | ||
26 | default: | ||
27 | return INSERT; | ||
28 | } | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * @since 2.4 | ||
33 | */ | ||
34 | public char asSign() { | ||
35 | switch (this) { | ||
36 | case INSERT: | ||
37 | return '+'; | ||
38 | default: | ||
39 | return '-'; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Returns the direction that is the product of this direction and the other direction. | ||
45 | * | ||
46 | * DELETE x DELETE = INSERT | ||
47 | * DELETE x INSERT = DELETE | ||
48 | * INSERT x DELETE = DELETE | ||
49 | * INSERT x INSERT = INSERT | ||
50 | * @since 2.4 | ||
51 | */ | ||
52 | public Direction multiply(final Direction other) { | ||
53 | switch (this) { | ||
54 | case DELETE: | ||
55 | return other.opposite(); | ||
56 | default: | ||
57 | return other; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java new file mode 100644 index 00000000..e24b2448 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java | |||
@@ -0,0 +1,86 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
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.util; | ||
11 | |||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap; | ||
17 | |||
18 | /** | ||
19 | * Eclipse Collections-based multiset for tuples. Can contain duplicate occurrences of the same matching. | ||
20 | * | ||
21 | * <p>Inherits Eclipse Collections' Object-to-Int primitive hashmap and counts the number of occurrences of each value. | ||
22 | * Element is deleted if # of occurences drops to 0. | ||
23 | * | ||
24 | * @author Gabor Bergmann. | ||
25 | * @since 1.7 | ||
26 | * @noreference | ||
27 | */ | ||
28 | public abstract class EclipseCollectionsBagMemory<T> extends ObjectIntHashMap<T> implements IMemory<T> { | ||
29 | |||
30 | public EclipseCollectionsBagMemory() { | ||
31 | super(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int getCount(T value) { | ||
36 | return super.getIfAbsent(value, 0); | ||
37 | } | ||
38 | @Override | ||
39 | public int getCountUnsafe(Object value) { | ||
40 | return super.getIfAbsent(value, 0); | ||
41 | } | ||
42 | @Override | ||
43 | public boolean containsNonZero(T value) { | ||
44 | return super.containsKey(value); | ||
45 | } | ||
46 | @Override | ||
47 | public boolean containsNonZeroUnsafe(Object value) { | ||
48 | return super.containsKey(value); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void clearAllOf(T value) { | ||
53 | super.remove(value); | ||
54 | } | ||
55 | |||
56 | |||
57 | @Override | ||
58 | public Iterator<T> iterator() { | ||
59 | return super.keySet().iterator(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public String toString() { | ||
64 | return "TM" + super.toString(); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Set<T> distinctValues() { | ||
69 | return super.keySet(); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
74 | super.forEachKeyValue(entryConsumer::accept); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public int hashCode() { | ||
79 | return IMemoryView.hashCode(this); | ||
80 | } | ||
81 | @Override | ||
82 | public boolean equals(Object obj) { | ||
83 | return IMemoryView.equals(this, obj); | ||
84 | } | ||
85 | |||
86 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java new file mode 100644 index 00000000..94ec33cd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public class EclipseCollectionsDeltaBag<T> extends EclipseCollectionsBagMemory<T> implements IDeltaBag<T> { | ||
16 | |||
17 | @Override | ||
18 | public boolean addOne(T value) { | ||
19 | return addSigned(value, +1); | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | public boolean addSigned(T value, int count) { | ||
24 | int oldCount = super.getIfAbsent(value, 0); | ||
25 | int newCount = oldCount + count; | ||
26 | |||
27 | boolean becomesZero = newCount == 0; | ||
28 | if (becomesZero) | ||
29 | super.removeKey(value); | ||
30 | else | ||
31 | super.put(value, newCount); | ||
32 | |||
33 | return becomesZero || oldCount == 0; | ||
34 | } | ||
35 | |||
36 | |||
37 | @Override | ||
38 | public boolean removeOne(T value) { | ||
39 | return addSigned(value, -1); | ||
40 | } | ||
41 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java new file mode 100644 index 00000000..5a623c9b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java | |||
@@ -0,0 +1,159 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import org.eclipse.collections.api.map.MutableMap; | ||
19 | import org.eclipse.collections.impl.factory.Maps; | ||
20 | import org.eclipse.collections.impl.factory.Sets; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * @since 1.7 | ||
27 | * @noreference This class is not intended to be referenced by clients. | ||
28 | */ | ||
29 | public class EclipseCollectionsFactory implements ICollectionsFramework { | ||
30 | |||
31 | @Override | ||
32 | public <K, V> Map<K, V> createMap() { | ||
33 | return Maps.mutable.empty(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public <K, V> Map<K, V> createMap(Map<K, V> initial) { | ||
38 | MutableMap<K, V> result = Maps.mutable.ofInitialCapacity(initial.size()); | ||
39 | result.putAll(initial); | ||
40 | return result; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public <K, V> TreeMap<K, V> createTreeMap() { | ||
45 | // eclipse collections is doing the same | ||
46 | return new TreeMap<>(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public <E> Set<E> createSet() { | ||
51 | return Sets.mutable.empty(); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public <E> Set<E> createSet(Collection<E> initial) { | ||
56 | return Sets.mutable.ofAll(initial); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public <T> IMultiset<T> createMultiset() { | ||
61 | return new EclipseCollectionsMultiset<T>(); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public <T> IDeltaBag<T> createDeltaBag() { | ||
66 | return new EclipseCollectionsDeltaBag<T>(); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public <O> List<O> createObserverList() { | ||
71 | return new ArrayList<O>(1); // keep concurrent modification exceptions for error detection | ||
72 | // Lists.mutable.empty | ||
73 | |||
74 | } | ||
75 | |||
76 | @Override | ||
77 | @SuppressWarnings({ "unchecked", "rawtypes" }) | ||
78 | public <K, V> IMultiLookup<K, V> createMultiLookup( | ||
79 | Class<? super K> fromKeys, | ||
80 | MemoryType toBuckets, | ||
81 | Class<? super V> ofValues) | ||
82 | { | ||
83 | boolean longKeys = Long.class.equals(fromKeys); | ||
84 | boolean objectKeys = Object.class.equals(fromKeys); | ||
85 | if (! (longKeys || objectKeys)) throw new IllegalArgumentException(fromKeys.getName()); | ||
86 | boolean longValues = Long.class.equals(ofValues); | ||
87 | boolean objectValues = Object.class.equals(ofValues); | ||
88 | if (! (longValues || objectValues)) throw new IllegalArgumentException(ofValues.getName()); | ||
89 | |||
90 | if (longKeys) { // K == java.lang.Long | ||
91 | if (longValues) { // V == java.lang.Long | ||
92 | switch(toBuckets) { | ||
93 | case MULTISETS: | ||
94 | return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfLongs(); | ||
95 | case SETS: | ||
96 | return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfLongs(); | ||
97 | default: | ||
98 | throw new IllegalArgumentException(toBuckets.toString()); | ||
99 | } | ||
100 | } else { // objectValues | ||
101 | switch(toBuckets) { | ||
102 | case MULTISETS: | ||
103 | return new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfObjects(); | ||
104 | case SETS: | ||
105 | return new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfObjects(); | ||
106 | default: | ||
107 | throw new IllegalArgumentException(toBuckets.toString()); | ||
108 | } | ||
109 | } | ||
110 | } else { // objectKeys | ||
111 | if (longValues) { // V == java.lang.Long | ||
112 | switch(toBuckets) { | ||
113 | case MULTISETS: | ||
114 | return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfLongs(); | ||
115 | case SETS: | ||
116 | return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfLongs(); | ||
117 | default: | ||
118 | throw new IllegalArgumentException(toBuckets.toString()); | ||
119 | } | ||
120 | } else { // objectValues | ||
121 | switch(toBuckets) { | ||
122 | case MULTISETS: | ||
123 | return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfObjects(); | ||
124 | case SETS: | ||
125 | return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfObjects(); | ||
126 | default: | ||
127 | throw new IllegalArgumentException(toBuckets.toString()); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | @Override | ||
134 | @SuppressWarnings("unchecked") | ||
135 | public <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType) { | ||
136 | if (Long.class.equals(values)) { // T == java.lang.Long | ||
137 | switch(memoryType) { | ||
138 | case MULTISETS: | ||
139 | return (IMemory<T>) new EclipseCollectionsLongMultiset(); | ||
140 | case SETS: | ||
141 | return (IMemory<T>) new EclipseCollectionsLongSetMemory(); | ||
142 | default: | ||
143 | throw new IllegalArgumentException(memoryType.toString()); | ||
144 | } | ||
145 | } else { // objectValues | ||
146 | switch(memoryType) { | ||
147 | case MULTISETS: | ||
148 | return new EclipseCollectionsMultiset<>(); | ||
149 | case SETS: | ||
150 | return new EclipseCollectionsSetMemory<>(); | ||
151 | default: | ||
152 | throw new IllegalArgumentException(memoryType.toString()); | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | |||
159 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java new file mode 100644 index 00000000..88773c5d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java | |||
@@ -0,0 +1,150 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.BiConsumer; | ||
14 | |||
15 | import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; | ||
16 | |||
17 | /** | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | * <p> TODO refactor common methods with {@link EclipseCollectionsMultiset} | ||
21 | * <p> TODO refactor into LongBagMemory etc. | ||
22 | */ | ||
23 | public class EclipseCollectionsLongMultiset extends LongIntHashMap implements IMultiset<Long> { | ||
24 | |||
25 | @Override | ||
26 | public boolean addOne(Long value) { | ||
27 | int oldCount = super.getIfAbsent(value, 0); | ||
28 | |||
29 | super.put(value, oldCount + 1); | ||
30 | |||
31 | return oldCount == 0; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public boolean addSigned(Long value, int count) { | ||
36 | int oldCount = super.getIfAbsent(value, 0); | ||
37 | int newCount = oldCount + count; | ||
38 | |||
39 | boolean becomesZero = newCount == 0; | ||
40 | if (newCount < 0) | ||
41 | throw new IllegalStateException(String.format( | ||
42 | "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", | ||
43 | count, value, newCount, this)); | ||
44 | else if (becomesZero) | ||
45 | super.removeKey(value); | ||
46 | else // (newCount > 0) | ||
47 | super.put(value, newCount); | ||
48 | |||
49 | return becomesZero || oldCount == 0; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean removeOne(Long value) { | ||
54 | return removeOneInternal(value, true); | ||
55 | } | ||
56 | /** | ||
57 | * @since 2.3 | ||
58 | */ | ||
59 | @Override | ||
60 | public boolean removeOneOrNop(Long value) { | ||
61 | return removeOneInternal(value, false); | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * @since 2.3 | ||
67 | */ | ||
68 | protected boolean removeOneInternal(Long value, boolean throwIfImpossible) { | ||
69 | int oldCount = super.getIfAbsent(value, 0); | ||
70 | if (oldCount == 0) { | ||
71 | if (throwIfImpossible) throw new IllegalStateException(String.format( | ||
72 | "Cannot remove value '%s' that is not contained in %s", | ||
73 | value, this)); | ||
74 | else return false; | ||
75 | } | ||
76 | |||
77 | int rest = oldCount - 1; | ||
78 | boolean empty = rest == 0; | ||
79 | |||
80 | if (!empty) { | ||
81 | super.put(value, rest); | ||
82 | } else { | ||
83 | super.remove(value); | ||
84 | } | ||
85 | |||
86 | return empty; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public void clearAllOf(Long value) { | ||
91 | super.remove(value); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public int getCount(Long value) { | ||
96 | return super.getIfAbsent(value, 0); | ||
97 | } | ||
98 | @Override | ||
99 | public int getCountUnsafe(Object value) { | ||
100 | return value instanceof Long ? getCount((Long) value) : 0; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public boolean containsNonZero(Long value) { | ||
105 | return super.containsKey(value); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public boolean containsNonZeroUnsafe(Object value) { | ||
110 | return value instanceof Long && containsNonZero((Long) value); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Iterator<Long> iterator() { | ||
115 | return EclipseCollectionsLongSetMemory.iteratorOf(super.keySet()); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public boolean addPositive(Long value, int count) { | ||
120 | if (count < 0) { | ||
121 | throw new IllegalArgumentException("The count value must be positive!"); | ||
122 | } | ||
123 | |||
124 | int oldCount = super.getIfAbsent(value, 0); | ||
125 | |||
126 | super.put(value, oldCount + count); | ||
127 | |||
128 | return oldCount == 0; | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public Set<Long> distinctValues() { | ||
133 | return new EclipseCollectionsLongSetMemory.SetWrapper(super.keySet()); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public void forEachEntryWithMultiplicities(BiConsumer<Long, Integer> entryConsumer) { | ||
138 | super.forEachKeyValue(entryConsumer::accept); | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public int hashCode() { | ||
143 | return IMemoryView.hashCode(this); | ||
144 | } | ||
145 | @Override | ||
146 | public boolean equals(Object obj) { | ||
147 | return IMemoryView.equals(this, obj); | ||
148 | } | ||
149 | |||
150 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java new file mode 100644 index 00000000..fceb54fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java | |||
@@ -0,0 +1,212 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import org.eclipse.collections.api.LongIterable; | ||
16 | import org.eclipse.collections.api.iterator.LongIterator; | ||
17 | import org.eclipse.collections.api.set.primitive.LongSet; | ||
18 | import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 2.0 | ||
23 | */ | ||
24 | public class EclipseCollectionsLongSetMemory extends LongHashSet implements ISetMemory<Long> { | ||
25 | |||
26 | @Override | ||
27 | public boolean addOne(Long value) { | ||
28 | return super.add(value); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public boolean addSigned(Long value, int count) { | ||
33 | if (count == 1) return addOne(value); | ||
34 | else if (count == -1) return removeOne(value); | ||
35 | else throw new IllegalStateException(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean removeOne(Long value) { | ||
40 | // Kept for binary compatibility | ||
41 | return ISetMemory.super.removeOne(value); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * @since 2.3 | ||
46 | */ | ||
47 | @Override | ||
48 | public boolean removeOneOrNop(Long value) { | ||
49 | return super.remove(value); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void clearAllOf(Long value) { | ||
54 | super.remove(value); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public int getCount(Long value) { | ||
59 | return super.contains(value) ? 1 : 0; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public int getCountUnsafe(Object value) { | ||
64 | return value instanceof Long ? getCount((Long) value) : 0; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean containsNonZero(Long value) { | ||
69 | return super.contains(value); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean containsNonZeroUnsafe(Object value) { | ||
74 | return value instanceof Long && containsNonZero((Long) value); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Iterator<Long> iterator() { | ||
79 | return iteratorOf(this); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public Set<Long> distinctValues() { | ||
84 | return new SetWrapper(this); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public boolean isEmpty() { | ||
89 | return super.isEmpty(); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Helper for iterating a LongIterable | ||
94 | */ | ||
95 | public static Iterator<Long> iteratorOf(LongIterable wrapped) { | ||
96 | return new Iterator<Long>() { | ||
97 | LongIterator longIterator = wrapped.longIterator(); | ||
98 | |||
99 | @Override | ||
100 | public boolean hasNext() { | ||
101 | return longIterator.hasNext(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public Long next() { | ||
106 | return longIterator.next(); | ||
107 | } | ||
108 | }; | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | public int hashCode() { | ||
113 | return IMemoryView.hashCode(this); | ||
114 | } | ||
115 | @Override | ||
116 | public boolean equals(Object obj) { | ||
117 | return IMemoryView.equals(this, obj); | ||
118 | } | ||
119 | |||
120 | |||
121 | /** | ||
122 | * Helper that presents a primitive collection as a Set view | ||
123 | * @author Gabor Bergmann | ||
124 | */ | ||
125 | public static final class SetWrapper implements Set<Long> { | ||
126 | private LongSet wrapped; | ||
127 | |||
128 | /** | ||
129 | * @param wrapped | ||
130 | */ | ||
131 | public SetWrapper(LongSet wrapped) { | ||
132 | this.wrapped = wrapped; | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public int size() { | ||
137 | return wrapped.size(); | ||
138 | } | ||
139 | |||
140 | @Override | ||
141 | public boolean isEmpty() { | ||
142 | return wrapped.isEmpty(); | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public boolean contains(Object o) { | ||
147 | return o instanceof Long && wrapped.contains((Long)o); | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public Iterator<Long> iterator() { | ||
152 | return iteratorOf(wrapped); | ||
153 | } | ||
154 | |||
155 | @Override | ||
156 | public boolean containsAll(Collection<?> c) { | ||
157 | for (Object object : c) { | ||
158 | if (contains(object)) | ||
159 | return true; | ||
160 | } | ||
161 | return false; | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public Object[] toArray() { | ||
166 | return toArray(new Long[wrapped.size()]); | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | @SuppressWarnings("unchecked") | ||
171 | public <T> T[] toArray(T[] a) { | ||
172 | int k = 0; | ||
173 | LongIterator iterator = wrapped.longIterator(); | ||
174 | while (iterator.hasNext()) | ||
175 | a[k++] = (T) Long.valueOf(iterator.next()); | ||
176 | return a; | ||
177 | } | ||
178 | |||
179 | @Override | ||
180 | public boolean add(Long e) { | ||
181 | throw new UnsupportedOperationException(); | ||
182 | } | ||
183 | |||
184 | @Override | ||
185 | public boolean remove(Object o) { | ||
186 | throw new UnsupportedOperationException(); | ||
187 | } | ||
188 | |||
189 | @Override | ||
190 | public boolean addAll(Collection<? extends Long> c) { | ||
191 | throw new UnsupportedOperationException(); | ||
192 | } | ||
193 | |||
194 | @Override | ||
195 | public boolean retainAll(Collection<?> c) { | ||
196 | throw new UnsupportedOperationException(); | ||
197 | } | ||
198 | |||
199 | @Override | ||
200 | public boolean removeAll(Collection<?> c) { | ||
201 | throw new UnsupportedOperationException(); | ||
202 | } | ||
203 | |||
204 | @Override | ||
205 | public void clear() { | ||
206 | throw new UnsupportedOperationException(); | ||
207 | } | ||
208 | |||
209 | |||
210 | } | ||
211 | |||
212 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java new file mode 100644 index 00000000..394135c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java | |||
@@ -0,0 +1,226 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import org.eclipse.collections.impl.map.mutable.UnifiedMap; | ||
12 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; | ||
15 | |||
16 | import java.util.Set; | ||
17 | import java.util.stream.Stream; | ||
18 | |||
19 | |||
20 | |||
21 | /** | ||
22 | * Eclipse Collections-based realizations of {@link IMultiLookup} | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | class EclipseCollectionsMultiLookup { | ||
28 | |||
29 | private EclipseCollectionsMultiLookup() {/* Hidden utility class constructor */} | ||
30 | |||
31 | private static class MarkedSetImpl<Value> extends EclipseCollectionsSetMemory<Value> implements MarkedMemory.MarkedSet<Value> {} | ||
32 | private static class MarkedMultisetImpl<Value> extends EclipseCollectionsMultiset<Value> implements MarkedMemory.MarkedMultiset<Value> {} | ||
33 | private static class MarkedLongSetImpl extends EclipseCollectionsLongSetMemory implements MarkedMemory.MarkedSet<Long> {} | ||
34 | private static class MarkedLongMultisetImpl extends EclipseCollectionsLongMultiset implements MarkedMemory.MarkedMultiset<Long> {} | ||
35 | |||
36 | public abstract static class FromObjects<Key, Value, Bucket extends MarkedMemory<Value>> | ||
37 | extends UnifiedMap<Key, Object> implements IMultiLookupAbstract<Key, Value, Bucket> { | ||
38 | |||
39 | @Override | ||
40 | public boolean equals(Object obj) { | ||
41 | return IMultiLookup.equals(this, obj); | ||
42 | } | ||
43 | @Override | ||
44 | public int hashCode() { | ||
45 | return IMultiLookup.hashCode(this); | ||
46 | } | ||
47 | |||
48 | |||
49 | @Override | ||
50 | public Object lowLevelPutIfAbsent(Key key, Value value) { | ||
51 | return super.putIfAbsent(key, value); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Object lowLevelGet(Key key) { | ||
56 | return super.get(key); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Object lowLevelGetUnsafe(Object key) { | ||
61 | return super.get(key); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public Object lowLevelRemove(Key key) { | ||
66 | return super.remove(key); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public void lowLevelPut(Key key, Object valueOrBucket) { | ||
71 | super.put(key, valueOrBucket); | ||
72 | } | ||
73 | @Override | ||
74 | public Iterable<Object> lowLevelValues() { | ||
75 | return super.values(); | ||
76 | } | ||
77 | @Override | ||
78 | public Set<Key> lowLevelKeySet() { | ||
79 | return super.keySet(); | ||
80 | } | ||
81 | @Override | ||
82 | public int lowLevelSize() { | ||
83 | return super.size(); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public Stream<Key> distinctKeysStream() { | ||
88 | // may be more efficient than the default spliterator | ||
89 | return super.keySet().stream(); | ||
90 | } | ||
91 | |||
92 | public abstract static class ToSets<Key, Value> extends FromObjects<Key, Value, MarkedSet<Value>> | ||
93 | implements IMultiLookupAbstract.ToSetsAbstract<Key, Value> | ||
94 | { | ||
95 | public static class OfObjects<Key, Value> extends ToSets<Key, Value> { | ||
96 | @Override | ||
97 | public MarkedSet<Value> createMarkedSet() { | ||
98 | return new MarkedSetImpl<Value>(); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | public static class OfLongs<Key> extends ToSets<Key, Long> { | ||
103 | @Override | ||
104 | public MarkedSet<Long> createMarkedSet() { | ||
105 | return new MarkedLongSetImpl(); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | } | ||
110 | |||
111 | public abstract static class ToMultisets<Key, Value> extends FromObjects<Key, Value, MarkedMultiset<Value>> | ||
112 | implements IMultiLookupAbstract.ToMultisetsAbstract<Key, Value> | ||
113 | { | ||
114 | public static class OfObjects<Key, Value> extends ToMultisets<Key, Value> { | ||
115 | @Override | ||
116 | public MarkedMultiset<Value> createMarkedMultiset() { | ||
117 | return new MarkedMultisetImpl<Value>(); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | public static class OfLongs<Key> extends ToMultisets<Key, Long> { | ||
122 | @Override | ||
123 | public MarkedMultiset<Long> createMarkedMultiset() { | ||
124 | return new MarkedLongMultisetImpl(); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | } | ||
129 | |||
130 | } | ||
131 | |||
132 | public abstract static class FromLongs<Value, Bucket extends MarkedMemory<Value>> | ||
133 | extends LongObjectHashMap<Object> implements IMultiLookupAbstract<Long, Value, Bucket> { | ||
134 | |||
135 | @Override | ||
136 | public boolean equals(Object obj) { | ||
137 | return IMultiLookup.equals(this, obj); | ||
138 | } | ||
139 | @Override | ||
140 | public int hashCode() { | ||
141 | return IMultiLookup.hashCode(this); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public Object lowLevelPutIfAbsent(Long key, Value value) { | ||
146 | Object old = super.get(key); | ||
147 | if (old == null) super.put(key, value); | ||
148 | return old; | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public Object lowLevelGet(Long key) { | ||
153 | return super.get(key); | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public Object lowLevelGetUnsafe(Object key) { | ||
158 | return key instanceof Long ? super.get((Long)key) : null; | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public Object lowLevelRemove(Long key) { | ||
163 | return super.remove(key); | ||
164 | } | ||
165 | |||
166 | @Override | ||
167 | public void lowLevelPut(Long key, Object valueOrBucket) { | ||
168 | super.put(key, valueOrBucket); | ||
169 | } | ||
170 | @Override | ||
171 | public Iterable<Object> lowLevelValues() { | ||
172 | return super.values(); | ||
173 | } | ||
174 | @Override | ||
175 | public int lowLevelSize() { | ||
176 | return super.size(); | ||
177 | } | ||
178 | @Override | ||
179 | public Iterable<Long> lowLevelKeySet() { | ||
180 | return () -> EclipseCollectionsLongSetMemory.iteratorOf(FromLongs.super.keysView()); | ||
181 | } | ||
182 | |||
183 | public abstract static class ToSets<Value> extends FromLongs<Value, MarkedSet<Value>> | ||
184 | implements IMultiLookupAbstract.ToSetsAbstract<Long, Value> | ||
185 | { | ||
186 | public static class OfObjects<Value> extends ToSets<Value> { | ||
187 | @Override | ||
188 | public MarkedSet<Value> createMarkedSet() { | ||
189 | return new MarkedSetImpl<Value>(); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | public static class OfLongs extends ToSets<Long> { | ||
194 | @Override | ||
195 | public MarkedSet<Long> createMarkedSet() { | ||
196 | return new MarkedLongSetImpl(); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | } | ||
201 | |||
202 | public abstract static class ToMultisets<Value> extends FromLongs<Value, MarkedMultiset<Value>> | ||
203 | implements IMultiLookupAbstract.ToMultisetsAbstract<Long, Value> | ||
204 | { | ||
205 | public static class OfObjects<Value> extends ToMultisets<Value> { | ||
206 | @Override | ||
207 | public MarkedMultiset<Value> createMarkedMultiset() { | ||
208 | return new MarkedMultisetImpl<Value>(); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | public static class OfLongs extends ToMultisets<Long> { | ||
213 | @Override | ||
214 | public MarkedMultiset<Long> createMarkedMultiset() { | ||
215 | return new MarkedLongMultisetImpl(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | } | ||
220 | |||
221 | } | ||
222 | |||
223 | |||
224 | } | ||
225 | |||
226 | |||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java new file mode 100644 index 00000000..46977c8b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java | |||
@@ -0,0 +1,93 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public class EclipseCollectionsMultiset<T> extends EclipseCollectionsBagMemory<T> implements IMultiset<T> { | ||
16 | |||
17 | @Override | ||
18 | public boolean addOne(T value) { | ||
19 | int oldCount = super.getIfAbsent(value, 0); | ||
20 | |||
21 | super.put(value, oldCount + 1); | ||
22 | |||
23 | return oldCount == 0; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public boolean addPositive(T value, int count) { | ||
28 | if (count < 0) { | ||
29 | throw new IllegalArgumentException("The count value must be positive!"); | ||
30 | } | ||
31 | |||
32 | int oldCount = super.getIfAbsent(value, 0); | ||
33 | |||
34 | super.put(value, oldCount + count); | ||
35 | |||
36 | return oldCount == 0; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public boolean addSigned(T value, int count) { | ||
41 | int oldCount = super.getIfAbsent(value, 0); | ||
42 | int newCount = oldCount + count; | ||
43 | |||
44 | boolean becomesZero = newCount == 0; | ||
45 | if (newCount < 0) | ||
46 | throw new IllegalStateException(String.format( | ||
47 | "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", | ||
48 | count, value, newCount, this)); | ||
49 | else if (becomesZero) | ||
50 | super.removeKey(value); | ||
51 | else // (newCount > 0) | ||
52 | super.put(value, newCount); | ||
53 | |||
54 | return becomesZero || oldCount == 0; | ||
55 | } | ||
56 | |||
57 | |||
58 | @Override | ||
59 | public boolean removeOne(T value) { | ||
60 | return removeOneInternal(value, true); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public boolean removeOneOrNop(T value) { | ||
65 | return removeOneInternal(value, false); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * @since 2.3 | ||
70 | */ | ||
71 | protected boolean removeOneInternal(T value, boolean throwIfImpossible) { | ||
72 | int oldCount = super.getIfAbsent(value, 0); | ||
73 | if (oldCount == 0) { | ||
74 | if (throwIfImpossible) throw new IllegalStateException(String.format( | ||
75 | "Cannot remove value '%s' that is not contained in %s", | ||
76 | value, this)); | ||
77 | else return false; | ||
78 | } | ||
79 | |||
80 | int rest = oldCount - 1; | ||
81 | boolean empty = rest == 0; | ||
82 | |||
83 | if (!empty) { | ||
84 | super.put(value, rest); | ||
85 | } else { | ||
86 | super.remove(value); | ||
87 | } | ||
88 | |||
89 | return empty; | ||
90 | } | ||
91 | |||
92 | |||
93 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java new file mode 100644 index 00000000..92f65246 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import org.eclipse.collections.impl.set.mutable.UnifiedSet; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.0 | ||
18 | */ | ||
19 | public class EclipseCollectionsSetMemory<Value> extends UnifiedSet<Value> implements ISetMemory<Value> { | ||
20 | @Override | ||
21 | public int getCount(Value value) { | ||
22 | return super.contains(value) ? 1 : 0; | ||
23 | } | ||
24 | @Override | ||
25 | public int getCountUnsafe(Object value) { | ||
26 | return super.contains(value) ? 1 : 0; | ||
27 | } | ||
28 | @Override | ||
29 | public boolean containsNonZero(Value value) { | ||
30 | return super.contains(value); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public boolean containsNonZeroUnsafe(Object value) { | ||
35 | return super.contains(value); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean addOne(Value value) { | ||
40 | return super.add(value); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean addSigned(Value value, int count) { | ||
45 | if (count == 1) return addOne(value); | ||
46 | else if (count == -1) return removeOne(value); | ||
47 | else throw new IllegalStateException(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean removeOne(Value value) { | ||
52 | // Kept for binary compatibility | ||
53 | return ISetMemory.super.removeOne(value); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean removeOneOrNop(Value value) { | ||
58 | return super.remove(value); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void clearAllOf(Value value) { | ||
63 | super.remove(value); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public Set<Value> distinctValues() { | ||
68 | return this; | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Value theContainedVersionOf(Value value) { | ||
73 | return super.get(value); | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | @SuppressWarnings("unchecked") | ||
78 | public Value theContainedVersionOfUnsafe(Object value) { | ||
79 | if (super.contains(value)) | ||
80 | return super.get((Value)value); | ||
81 | else return null; | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public int hashCode() { | ||
86 | return IMemoryView.hashCode(this); | ||
87 | } | ||
88 | @Override | ||
89 | public boolean equals(Object obj) { | ||
90 | return IMemoryView.equals(this, obj); | ||
91 | } | ||
92 | |||
93 | |||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java new file mode 100644 index 00000000..a17b3a3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java | |||
@@ -0,0 +1,93 @@ | |||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * A singleton immutable empty memory. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 2.0 | ||
19 | * | ||
20 | */ | ||
21 | public class EmptyMemory<T> implements IMemoryView<T> { | ||
22 | |||
23 | @SuppressWarnings("rawtypes") | ||
24 | private static final EmptyMemory INSTANCE = new EmptyMemory(); | ||
25 | |||
26 | @SuppressWarnings("unchecked") | ||
27 | public static <T> EmptyMemory<T> instance() { | ||
28 | return INSTANCE; | ||
29 | } | ||
30 | |||
31 | |||
32 | |||
33 | /** | ||
34 | * Singleton; hidden constructor | ||
35 | */ | ||
36 | private EmptyMemory() { | ||
37 | super(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Iterator<T> iterator() { | ||
42 | return Collections.<T>emptySet().iterator(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getCount(T value) { | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public int getCountUnsafe(Object value) { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean containsNonZero(T value) { | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public boolean containsNonZeroUnsafe(Object value) { | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public int size() { | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean isEmpty() { | ||
72 | return true; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Set<T> distinctValues() { | ||
77 | return Collections.emptySet(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public int hashCode() { | ||
82 | return IMemoryView.hashCode(this); | ||
83 | } | ||
84 | @Override | ||
85 | public boolean equals(Object obj) { | ||
86 | return IMemoryView.equals(this, obj); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public String toString() { | ||
91 | return "{}"; | ||
92 | } | ||
93 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java new file mode 100644 index 00000000..8c2e54ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.util; | ||
10 | |||
11 | import java.util.function.Supplier; | ||
12 | |||
13 | /** | ||
14 | * A cache is a simple key-value pair that stores calculated values for specific key objects | ||
15 | * | ||
16 | * <p> | ||
17 | * <b>NOTE</b> These caches are not expected to be used outside query backend implementations | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * @since 1.7 | ||
21 | * @noreference This interface is not intended to be referenced by clients. | ||
22 | */ | ||
23 | public interface ICache { | ||
24 | |||
25 | /** | ||
26 | * Return a selected value for the key object. If the value is not available in the cache yet, the given provider is | ||
27 | * called once | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider); | ||
31 | |||
32 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java new file mode 100644 index 00000000..99a4cb3b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * An {@link IMemory} that represents the difference between two states of a set or {@link IMultiset}, and therefore | ||
13 | * may contain values with a negative multiplicity. | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public interface IDeltaBag<T> extends IMemory<T> { | ||
19 | |||
20 | @Override | ||
21 | default boolean removeOneOrNop(T value) { | ||
22 | // makes no difference for delta bags | ||
23 | return removeOne(value); | ||
24 | } | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java new file mode 100644 index 00000000..ea788e53 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * A memory containing a positive or negative number of equal() copies for some values. | ||
13 | * During iterations, each distinct value is iterated only once. | ||
14 | * | ||
15 | * <p> Refined by: <ul> | ||
16 | * <li>{@link IMultiset}, which always contains values with a nonnegative multiplicity. </li> | ||
17 | * <li>{@link IDeltaBag}, which may contain values with negative multiplicity. </li> | ||
18 | * <li>{@link ISetMemory}, which is just a set (allowed multiplicities: 0 and 1). </li> | ||
19 | * </ul> | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 1.7 | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IMemory<T> extends IMemoryView<T>, Clearable { | ||
26 | |||
27 | /** | ||
28 | * Adds one value occurrence to the memory. | ||
29 | * | ||
30 | * @return true if the tuple was not present before in the memory, or | ||
31 | * (in case of {@link IDeltaBag}) is no longer present in the memory | ||
32 | */ | ||
33 | boolean addOne(T value); | ||
34 | |||
35 | /** | ||
36 | * Adds the given number of occurrences to the memory. The count value may or may not be negative. | ||
37 | * <p> Precondition if {@link IMultiset}: at least the given amount of occurrences exist, if count is negative. | ||
38 | * <p> Precondition if {@link ISetMemory}: count is +1 or -1, the latter is only allowed if the set contains the value. | ||
39 | * | ||
40 | * @param count | ||
41 | * the number of occurrences | ||
42 | * @return true if the tuple was not present before in the memory, or is no longer present in the memory | ||
43 | * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and the number of occurrences in the memory would underflow to negative | ||
44 | */ | ||
45 | boolean addSigned(T value, int count); | ||
46 | |||
47 | /** | ||
48 | * Removes one occurrence of the given value from the memory. | ||
49 | * <p> Precondition if {@link IMultiset} or {@link ISetMemory}: the value must have a positive amount of occurrences in the memory. | ||
50 | * | ||
51 | * @return true if this was the the last occurrence of the value, or | ||
52 | * (in case of {@link IDeltaBag}) is the first negative occurrence of the value | ||
53 | * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory | ||
54 | */ | ||
55 | boolean removeOne(T value); | ||
56 | |||
57 | /** | ||
58 | * Removes one occurrence of the given value from the memory, if possible. | ||
59 | * | ||
60 | * <p> Memory is unchanged and false is returned if | ||
61 | * {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory | ||
62 | * | ||
63 | * @return true if this was the the last occurrence of the value, or | ||
64 | * (in case of {@link IDeltaBag}) is the first negative occurrence of the value | ||
65 | * | ||
66 | * @since 2.3 | ||
67 | */ | ||
68 | boolean removeOneOrNop(T value); | ||
69 | |||
70 | /** | ||
71 | * Removes all occurrences of the given value from the memory. | ||
72 | */ | ||
73 | void clearAllOf(T value); | ||
74 | |||
75 | /** | ||
76 | * Empties out the memory. | ||
77 | */ | ||
78 | @Override | ||
79 | void clear(); | ||
80 | |||
81 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java new file mode 100644 index 00000000..add575c6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java | |||
@@ -0,0 +1,205 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.BiConsumer; | ||
16 | import java.util.stream.Stream; | ||
17 | import java.util.stream.StreamSupport; | ||
18 | |||
19 | /** | ||
20 | * A read-only view on a memory containing a positive or negative number of equal() copies for some values. | ||
21 | * During iterations, each distinct value is iterated only once. | ||
22 | * | ||
23 | * <p> See {@link IMemory}. | ||
24 | * | ||
25 | * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMemoryView)} and {@link #equals(IMemoryView, Object)} here. | ||
26 | * | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | * @since 2.0 | ||
30 | */ | ||
31 | public interface IMemoryView<T> extends Iterable<T> { | ||
32 | |||
33 | /** | ||
34 | * Returns the number of occurrences of the given value. | ||
35 | * | ||
36 | * @return the number of occurrences | ||
37 | */ | ||
38 | int getCount(T value); | ||
39 | |||
40 | /** | ||
41 | * Returns the number of occurrences of the given value (which may be of any type). | ||
42 | * | ||
43 | * @return the number of occurrences | ||
44 | */ | ||
45 | int getCountUnsafe(Object value); | ||
46 | |||
47 | /** | ||
48 | * @return true if the given value is contained with a nonzero multiplicity | ||
49 | */ | ||
50 | boolean containsNonZero(T value); | ||
51 | |||
52 | /** | ||
53 | * @return true if the given value (which may be of any type) is contained with a nonzero multiplicity | ||
54 | */ | ||
55 | boolean containsNonZeroUnsafe(Object value); | ||
56 | |||
57 | /** | ||
58 | * @return the number of distinct values | ||
59 | */ | ||
60 | int size(); | ||
61 | |||
62 | /** | ||
63 | * | ||
64 | * @return iff contains at least one value with non-zero occurrences | ||
65 | */ | ||
66 | boolean isEmpty(); | ||
67 | |||
68 | /** | ||
69 | * The set of distinct values | ||
70 | */ | ||
71 | Set<T> distinctValues(); | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Where supported, returns the stored element that is equal to the given value, or null if none. | ||
76 | * Useful for canonicalization in case of non-identity equals(). | ||
77 | * | ||
78 | * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none. | ||
79 | * | ||
80 | * @return a value equal to the argument if such a value is stored, or null if none | ||
81 | */ | ||
82 | default T theContainedVersionOf(T value) { | ||
83 | if (containsNonZero(value)) return value; else return null; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Where supported, returns the stored element that is equal to the given value (of any type), | ||
88 | * or null if none. | ||
89 | * Useful for canonicalization in case of non-identity equals(). | ||
90 | * | ||
91 | * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none. | ||
92 | * | ||
93 | * @return a value equal to the argument if such a value is stored, or null if none | ||
94 | */ | ||
95 | @SuppressWarnings("unchecked") | ||
96 | default T theContainedVersionOfUnsafe(Object value) { | ||
97 | if (containsNonZeroUnsafe(value)) return (T) value; else return null; | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * @return an unmodifiable view of contained values with their multiplicities | ||
103 | */ | ||
104 | default Iterable<Map.Entry<T, Integer>> entriesWithMultiplicities() { | ||
105 | return () -> { | ||
106 | Iterator<T> wrapped = distinctValues().iterator(); | ||
107 | return new Iterator<Map.Entry<T, Integer>> () { | ||
108 | @Override | ||
109 | public boolean hasNext() { | ||
110 | return wrapped.hasNext(); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Map.Entry<T, Integer> next() { | ||
115 | T key = wrapped.next(); | ||
116 | int count = getCount(key); | ||
117 | return new Map.Entry<T, Integer>(){ | ||
118 | @Override | ||
119 | public T getKey() { | ||
120 | return key; | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public Integer getValue() { | ||
125 | return count; | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public Integer setValue(Integer value) { | ||
130 | throw new UnsupportedOperationException(); | ||
131 | } | ||
132 | |||
133 | @Override | ||
134 | public String toString() { | ||
135 | return String.format("%d of %s", count, key); | ||
136 | } | ||
137 | |||
138 | }; | ||
139 | } | ||
140 | |||
141 | }; | ||
142 | }; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Process contained values with their multiplicities | ||
147 | */ | ||
148 | default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
149 | for (T value : distinctValues()) { | ||
150 | entryConsumer.accept(value, getCount(value)); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | |||
155 | /** | ||
156 | * For compatibility with legacy code relying on element-to-integer maps. | ||
157 | * @return an unmodifiable view of contained values with their multiplicities | ||
158 | */ | ||
159 | public default Map<T, Integer> asMap() { | ||
160 | return new MemoryViewBackedMapView<>(this); | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * For compatibility with legacy code relying on element-to-integer maps. | ||
165 | * @return an unmodifiable view of contained values with their multiplicities | ||
166 | */ | ||
167 | public static <T> IMemoryView<T> fromMap(Map<T, Integer> wrapped) { | ||
168 | return new MapBackedMemoryView<>(wrapped); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * @return a stream of values, iterable once | ||
173 | * @since 2.1 | ||
174 | */ | ||
175 | public default Stream<T> asStream() { | ||
176 | return StreamSupport.stream(spliterator(), false); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Provides semantic equality comparison. | ||
181 | */ | ||
182 | public static <T> boolean equals(IMemoryView<T> self, Object obj) { | ||
183 | if (obj instanceof IMemoryView<?>) { | ||
184 | IMemoryView<?> other = (IMemoryView<?>) obj; | ||
185 | if (other.size() != self.size()) return false; | ||
186 | for (Entry<?, Integer> entry : other.entriesWithMultiplicities()) { | ||
187 | if ( !entry.getValue().equals(self.getCountUnsafe(entry.getKey()))) | ||
188 | return false; | ||
189 | } | ||
190 | return true; | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Provides semantic hashCode() comparison. | ||
197 | */ | ||
198 | public static <T> int hashCode(IMemoryView<T> memory) { | ||
199 | int hashCode = 0; | ||
200 | for (T value : memory.distinctValues()) { | ||
201 | hashCode += value.hashCode() ^ Integer.hashCode(memory.getCount(value)); | ||
202 | } | ||
203 | return hashCode; | ||
204 | } | ||
205 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java new file mode 100644 index 00000000..1ce1d2c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java | |||
@@ -0,0 +1,216 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
14 | |||
15 | /** | ||
16 | * A multi-map that associates sets / multisets / delta sets of values to each key. | ||
17 | * | ||
18 | * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMultiLookup)} and {@link #equals(IMultiLookup, Object)} here. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.0 | ||
22 | * @noimplement This interface is not intended to be implemented by clients. | ||
23 | */ | ||
24 | public interface IMultiLookup<Key, Value> { | ||
25 | |||
26 | /** | ||
27 | * Returns true if this collection is empty, false otherwise. | ||
28 | * @since 2.2 | ||
29 | */ | ||
30 | boolean isEmpty(); | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Returns true if there are any values associated with the given key. | ||
35 | * @param key a key for which associated values are sought | ||
36 | * @since 2.3 | ||
37 | */ | ||
38 | boolean lookupExists(Key key); | ||
39 | |||
40 | /** | ||
41 | * Returns a (read-only) bucket of values associated with the given key. | ||
42 | * Clients must not modify the returned bucket. | ||
43 | * @param key a key for which associated values are sought | ||
44 | * @return null if key not found, a bucket of values otherwise | ||
45 | */ | ||
46 | IMemoryView<Value> lookup(Key key); | ||
47 | |||
48 | /** | ||
49 | * Returns a (read-only) bucket of values associated with the given key. | ||
50 | * Clients must not modify the returned bucket. | ||
51 | * @param key a key for which associated values are sought | ||
52 | * @return a bucket of values, never null | ||
53 | */ | ||
54 | default IMemoryView<Value> lookupOrEmpty(Key key) { | ||
55 | IMemoryView<Value> bucket = lookup(key); | ||
56 | return bucket == null ? EmptyMemory.instance() : bucket; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Returns a (read-only) bucket of values associated with the given key, while simultaneously removing them. | ||
61 | * Clients must not modify the returned bucket. | ||
62 | * @param key a key for which associated values are sought | ||
63 | * @return a bucket of values, never null | ||
64 | * @since 2.3 | ||
65 | */ | ||
66 | IMemoryView<Value> lookupAndRemoveAll(Key key); | ||
67 | |||
68 | /** | ||
69 | * Returns a (read-only) bucket of values associated with the given key, which can be of any type. | ||
70 | * Clients must not modify the returned bucket. | ||
71 | * @param key a key for which associated values are sought (may or may not be of Key type) | ||
72 | * @return null if key not found, a bucket of values otherwise | ||
73 | */ | ||
74 | IMemoryView<Value> lookupUnsafe(Object key); | ||
75 | |||
76 | /** | ||
77 | * Returns a (read-only) bucket of values associated with the given key. | ||
78 | * Clients must not modify the returned bucket. | ||
79 | * @param key a key for which associated values are sought (may or may not be of Key type) | ||
80 | * @return a bucket of values, never null | ||
81 | */ | ||
82 | default IMemoryView<Value> lookupUnsafeOrEmpty(Object key) { | ||
83 | IMemoryView<Value> bucket = lookupUnsafe(key); | ||
84 | return bucket == null ? EmptyMemory.instance() : bucket; | ||
85 | } | ||
86 | |||
87 | |||
88 | |||
89 | /** | ||
90 | * @return the set of distinct keys that have values associated. | ||
91 | */ | ||
92 | Iterable<Key> distinctKeys(); | ||
93 | |||
94 | /** | ||
95 | * @return the set of distinct keys that have values associated. | ||
96 | * @since 2.3 | ||
97 | */ | ||
98 | Stream<Key> distinctKeysStream(); | ||
99 | |||
100 | /** | ||
101 | * @return the number of distinct keys that have values associated. | ||
102 | */ | ||
103 | int countKeys(); | ||
104 | |||
105 | /** | ||
106 | * Iterates once over each distinct value. | ||
107 | */ | ||
108 | Iterable<Value> distinctValues(); | ||
109 | |||
110 | /** | ||
111 | * Iterates once over each distinct value. | ||
112 | * @since 2.3 | ||
113 | */ | ||
114 | Stream<Value> distinctValuesStream(); | ||
115 | |||
116 | |||
117 | |||
118 | /** | ||
119 | * How significant was the change? * | ||
120 | * @author Gabor Bergmann | ||
121 | */ | ||
122 | public enum ChangeGranularity { | ||
123 | /** | ||
124 | * First key-value pair with given key inserted, or last pair with given key deleted. | ||
125 | * (In case of delta maps, also if last negative key-value pair with given key neutralized.) | ||
126 | */ | ||
127 | KEY, | ||
128 | /** | ||
129 | * First occurrence of given key-value pair inserted, or last occurrence of the pair deleted, while key still has values associated. | ||
130 | * (In case of delta maps, also if last negative occurrence of key-value pair neutralized.) | ||
131 | */ | ||
132 | VALUE, | ||
133 | /** | ||
134 | * Duplicate key-value pair inserted or deleted. | ||
135 | */ | ||
136 | DUPLICATE | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Adds key-value pair to the lookup structure, or fails if not possible. | ||
141 | * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), | ||
142 | * the operation throws an {@link IllegalStateException}. | ||
143 | * @return the granularity of the change | ||
144 | * @throws IllegalStateException if addition would cause duplication that is not permitted | ||
145 | */ | ||
146 | public ChangeGranularity addPair(Key key, Value value); | ||
147 | /** | ||
148 | * Adds key-value pair to the lookup structure. | ||
149 | * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), | ||
150 | * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. | ||
151 | * @return the granularity of the change, or {@link ChangeGranularity#DUPLICATE} if addition would result in a duplicate and therefore ignored | ||
152 | * @since 2.3 | ||
153 | */ | ||
154 | public ChangeGranularity addPairOrNop(Key key, Value value); | ||
155 | /** | ||
156 | * Removes key-value pair from the lookup structure, or fails if not possible. | ||
157 | * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type | ||
158 | * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), an {@link IllegalStateException} is thrown. | ||
159 | * @return the granularity of the change | ||
160 | * @throws IllegalStateException if removing non-existing element that is not permitted | ||
161 | */ | ||
162 | public ChangeGranularity removePair(Key key, Value value); | ||
163 | /** | ||
164 | * Removes key-value pair from the lookup structure. | ||
165 | * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type | ||
166 | * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), | ||
167 | * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. | ||
168 | * @return the granularity of the change | ||
169 | * @throws IllegalStateException if removing non-existing element that is not permitted | ||
170 | * @since 2.3 | ||
171 | */ | ||
172 | public ChangeGranularity removePairOrNop(Key key, Value value); | ||
173 | |||
174 | /** | ||
175 | * Updates multiplicity of key-value pair by a positive amount. | ||
176 | * | ||
177 | * <p> PRE: count > 0 | ||
178 | * | ||
179 | * @return the granularity of the change | ||
180 | * @throws IllegalStateException if addition would cause duplication that is not permitted | ||
181 | */ | ||
182 | public ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count); | ||
183 | |||
184 | /** | ||
185 | * Empties out the lookup structure. | ||
186 | */ | ||
187 | public void clear(); | ||
188 | |||
189 | /** | ||
190 | * Provides semantic equality comparison. | ||
191 | */ | ||
192 | public static <Key, Value> boolean equals(IMultiLookup<Key, Value> self, Object obj) { | ||
193 | if (obj instanceof IMultiLookup<?, ?>) { | ||
194 | IMultiLookup<?, ?> other = (IMultiLookup<?, ?>) obj; | ||
195 | if (other.countKeys() != self.countKeys()) return false; | ||
196 | for (Object key : other.distinctKeys()) { | ||
197 | if (! other.lookupUnsafe(key).equals(self.lookupUnsafe(key))) | ||
198 | return false; | ||
199 | } | ||
200 | return true; | ||
201 | } | ||
202 | return false; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Provides semantic hashCode() comparison. | ||
207 | */ | ||
208 | public static <Key, Value> int hashCode(IMultiLookup<Key, Value> memory) { | ||
209 | int hashCode = 0; | ||
210 | for (Key key : memory.distinctKeys()) { | ||
211 | hashCode += key.hashCode() ^ memory.lookup(key).hashCode(); | ||
212 | } | ||
213 | return hashCode; | ||
214 | } | ||
215 | |||
216 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java new file mode 100644 index 00000000..8b1944c1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java | |||
@@ -0,0 +1,485 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import java.util.Objects; | ||
15 | import java.util.stream.Stream; | ||
16 | import java.util.stream.StreamSupport; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; | ||
19 | |||
20 | /** | ||
21 | * Specialized multimap implementation that saves memory | ||
22 | * by storing singleton value objects (multiplicity 1) instead of multiset buckets | ||
23 | * whenever there is only one value associated with a key. | ||
24 | * | ||
25 | * <p> See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types. | ||
26 | * | ||
27 | * <p> Implemented as a Key->Object map with invariant: <ul> | ||
28 | * <li> key maps to null if associated with no values; | ||
29 | * <li> key maps to a single Value iff it is associated with a single value of multiplicity +1; | ||
30 | * <li> key maps to Bucket otherwise | ||
31 | * </ul> | ||
32 | * | ||
33 | * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps. | ||
34 | * | ||
35 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
36 | * Realized as an interface with default implementations, instead of an abstract class, | ||
37 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
38 | * | ||
39 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
40 | * and bind the lowLevel* methods accordingly. | ||
41 | * | ||
42 | * @noreference This interface is not intended to be referenced by clients. | ||
43 | * @noimplement This interface is not intended to be implemented by clients. | ||
44 | * | ||
45 | * @author Gabor Bergmann | ||
46 | * @since 2.0 | ||
47 | * | ||
48 | * | ||
49 | */ | ||
50 | public interface IMultiLookupAbstract<Key, Value, Bucket extends MarkedMemory<Value>> extends IMultiLookup<Key, Value> { | ||
51 | |||
52 | // the following methods must be bound to a concrete Map<Key,Object>-like structure (primitive implementation allowed) | ||
53 | |||
54 | /** | ||
55 | * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map | ||
56 | */ | ||
57 | abstract Object lowLevelGet(Key key); | ||
58 | |||
59 | /** | ||
60 | * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map | ||
61 | */ | ||
62 | abstract Object lowLevelGetUnsafe(Object key); | ||
63 | |||
64 | /** | ||
65 | * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map | ||
66 | */ | ||
67 | abstract Object lowLevelRemove(Key key); | ||
68 | |||
69 | /** | ||
70 | * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map | ||
71 | */ | ||
72 | abstract Object lowLevelPutIfAbsent(Key key, Value value); | ||
73 | |||
74 | /** | ||
75 | * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map | ||
76 | */ | ||
77 | abstract void lowLevelPut(Key key, Object valueOrBucket); | ||
78 | |||
79 | /** | ||
80 | * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map | ||
81 | */ | ||
82 | abstract Iterable<Object> lowLevelValues(); | ||
83 | |||
84 | /** | ||
85 | * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map | ||
86 | */ | ||
87 | abstract Iterable<Key> lowLevelKeySet(); | ||
88 | |||
89 | /** | ||
90 | * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map | ||
91 | */ | ||
92 | abstract int lowLevelSize(); | ||
93 | |||
94 | |||
95 | // generic multi-lookup logic | ||
96 | |||
97 | @Override | ||
98 | default boolean lookupExists(Key key) { | ||
99 | Object object = lowLevelGet(key); | ||
100 | return null != object; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public default IMemoryView<Value> lookup(Key key) { | ||
105 | Object object = lowLevelGet(key); | ||
106 | if (object == null) return null; | ||
107 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
108 | return yieldSingleton((Value)object); | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | default IMemoryView<Value> lookupAndRemoveAll(Key key) { | ||
113 | Object object = lowLevelRemove(key); | ||
114 | if (object == null) return EmptyMemory.instance(); | ||
115 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
116 | return yieldSingleton((Value)object); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public default IMemoryView<Value> lookupUnsafe(Object key) { | ||
121 | Object object = lowLevelGetUnsafe(key); | ||
122 | if (object == null) return null; | ||
123 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
124 | return yieldSingleton((Value)object); | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public default ChangeGranularity addPair(Key key, Value value) { | ||
129 | return addPairInternal(key, value, true); | ||
130 | } | ||
131 | |||
132 | @Override | ||
133 | default ChangeGranularity addPairOrNop(Key key, Value value) { | ||
134 | return addPairInternal(key, value, false); | ||
135 | } | ||
136 | |||
137 | public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) { | ||
138 | Object old = lowLevelPutIfAbsent(key, value); | ||
139 | boolean keyChange = (old == null); | ||
140 | |||
141 | if (keyChange) { // key was not present | ||
142 | return ChangeGranularity.KEY; | ||
143 | } else { // key was already present | ||
144 | Bucket bucket; | ||
145 | if (old instanceof MarkedMemory) { // ... as collection | ||
146 | bucket = (Bucket) old; | ||
147 | } else { // ... as singleton | ||
148 | if (!this.duplicatesAllowed() && Objects.equals(value, old)) { | ||
149 | if (throwIfImpossible) | ||
150 | throw new IllegalStateException(); | ||
151 | else | ||
152 | return ChangeGranularity.DUPLICATE; | ||
153 | } | ||
154 | bucket = createSingletonBucket((Value) old); | ||
155 | lowLevelPut(key, bucket); | ||
156 | } | ||
157 | // will throw if forbidden duplicate, return false if allowed duplicate | ||
158 | if (addToBucket(bucket, value, throwIfImpossible)) { | ||
159 | // deltas may become empty or a singleton after addition! | ||
160 | if (negativesAllowed()) { | ||
161 | if (bucket.isEmpty()) { | ||
162 | lowLevelRemove(key); | ||
163 | return ChangeGranularity.KEY; | ||
164 | } else { | ||
165 | handleSingleton(key, bucket); | ||
166 | return ChangeGranularity.VALUE; | ||
167 | } | ||
168 | } else return ChangeGranularity.VALUE; | ||
169 | } else return ChangeGranularity.DUPLICATE; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | // TODO deltas not supproted yet | ||
175 | default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) { | ||
176 | if (count == 1) return addPair(key, value); | ||
177 | // count > 1, always end up with non-singleton bucket | ||
178 | |||
179 | Object old = lowLevelGet(key); | ||
180 | boolean keyChange = (old == null); | ||
181 | |||
182 | Bucket bucket; | ||
183 | if (keyChange) { // ... nothing associated to key yet | ||
184 | bucket = createSingletonBucket(value); | ||
185 | lowLevelPut(key, bucket); | ||
186 | --count; // one less to increment later | ||
187 | } else if (old instanceof MarkedMemory) { // ... as collection | ||
188 | bucket = (Bucket) old; | ||
189 | } else { // ... as singleton | ||
190 | bucket = createSingletonBucket((Value) old); | ||
191 | lowLevelPut(key, bucket); | ||
192 | } | ||
193 | |||
194 | boolean newValue = bucket.addSigned(value, count); | ||
195 | |||
196 | if (keyChange) return ChangeGranularity.KEY; | ||
197 | else if (newValue) return ChangeGranularity.VALUE; | ||
198 | else return ChangeGranularity.DUPLICATE; | ||
199 | } | ||
200 | |||
201 | @Override | ||
202 | public default ChangeGranularity removePair(Key key, Value value) { | ||
203 | return removePairInternal(key, value, true); | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | default ChangeGranularity removePairOrNop(Key key, Value value) { | ||
208 | return removePairInternal(key, value, false); | ||
209 | } | ||
210 | |||
211 | public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) { | ||
212 | Object old = lowLevelGet(key); | ||
213 | if (old instanceof MarkedMemory) { // ... as collection | ||
214 | @SuppressWarnings("unchecked") | ||
215 | Bucket bucket = (Bucket) old; | ||
216 | // will throw if removing non-existent, return false if removing duplicate | ||
217 | boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible); | ||
218 | handleSingleton(key, bucket); | ||
219 | if (valueChange) | ||
220 | return ChangeGranularity.VALUE; | ||
221 | else | ||
222 | return ChangeGranularity.DUPLICATE; | ||
223 | } else if (value.equals(old)) { // matching singleton | ||
224 | lowLevelRemove(key); | ||
225 | return ChangeGranularity.KEY; | ||
226 | } else { // different singleton, will produce a delta if possible | ||
227 | if (negativesAllowed()) { | ||
228 | Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported | ||
229 | lowLevelPut(key, deltaBucket); | ||
230 | return ChangeGranularity.VALUE; // no key change | ||
231 | } else { | ||
232 | if (throwIfImpossible) | ||
233 | throw new IllegalStateException(); | ||
234 | else | ||
235 | return ChangeGranularity.DUPLICATE; | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | public default void handleSingleton(Key key, Bucket bucket) { | ||
241 | Value remainingSingleton = asSingleton(bucket); | ||
242 | if (remainingSingleton != null) { // only one remains | ||
243 | lowLevelPut(key, remainingSingleton); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | @Override | ||
248 | public default Iterable<Value> distinctValues() { | ||
249 | return new Iterable<Value>() { | ||
250 | private final Iterator<Value> EMPTY_ITERATOR = Collections.<Value>emptySet().iterator(); | ||
251 | @Override | ||
252 | public Iterator<Value> iterator() { | ||
253 | return new Iterator<Value>() { | ||
254 | Iterator<Object> bucketIterator = lowLevelValues().iterator(); | ||
255 | Iterator<Value> elementIterator = EMPTY_ITERATOR; | ||
256 | |||
257 | @Override | ||
258 | public boolean hasNext() { | ||
259 | return (elementIterator.hasNext() || bucketIterator.hasNext()); | ||
260 | } | ||
261 | |||
262 | @Override | ||
263 | public Value next() { | ||
264 | if (elementIterator.hasNext()) | ||
265 | return elementIterator.next(); | ||
266 | else if (bucketIterator.hasNext()) { | ||
267 | Object bucket = bucketIterator.next(); | ||
268 | if (bucket instanceof MarkedMemory) { | ||
269 | elementIterator = | ||
270 | ((MarkedMemory) bucket).distinctValues().iterator(); | ||
271 | return elementIterator.next(); | ||
272 | } else { | ||
273 | elementIterator = EMPTY_ITERATOR; | ||
274 | return (Value) bucket; | ||
275 | } | ||
276 | } else | ||
277 | throw new NoSuchElementException(); | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * Not implemented | ||
282 | */ | ||
283 | @Override | ||
284 | public void remove() { | ||
285 | throw new UnsupportedOperationException(); | ||
286 | } | ||
287 | |||
288 | }; | ||
289 | } | ||
290 | }; | ||
291 | } | ||
292 | |||
293 | @Override | ||
294 | default Stream<Value> distinctValuesStream() { | ||
295 | return StreamSupport.stream(distinctValues().spliterator(), false); | ||
296 | } | ||
297 | |||
298 | @Override | ||
299 | default Iterable<Key> distinctKeys() { | ||
300 | return lowLevelKeySet(); | ||
301 | } | ||
302 | |||
303 | @Override | ||
304 | default Stream<Key> distinctKeysStream() { | ||
305 | return StreamSupport.stream(distinctKeys().spliterator(), false); | ||
306 | } | ||
307 | |||
308 | @Override | ||
309 | default int countKeys() { | ||
310 | return lowLevelSize(); | ||
311 | } | ||
312 | |||
313 | // the following methods are customized for bucket type | ||
314 | |||
315 | /** | ||
316 | * @return iff negative multiplicites are allowed | ||
317 | */ | ||
318 | abstract boolean negativesAllowed(); | ||
319 | |||
320 | /** | ||
321 | * @return iff larger-than-1 multiplicites are allowed | ||
322 | * @since 2.3 | ||
323 | */ | ||
324 | abstract boolean duplicatesAllowed(); | ||
325 | |||
326 | /** | ||
327 | * Increases the multiplicity of the value in the bucket. | ||
328 | * @return true iff non-duplicate | ||
329 | * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified | ||
330 | */ | ||
331 | abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible); | ||
332 | |||
333 | /** | ||
334 | * Decreases the multiplicity of the value in the bucket. | ||
335 | * @return false if removing duplicate value | ||
336 | * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified | ||
337 | */ | ||
338 | abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible); | ||
339 | |||
340 | /** | ||
341 | * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1 | ||
342 | * @return the singleton value, or null if the bucket is not singleton | ||
343 | */ | ||
344 | abstract Value asSingleton(Bucket bucket); | ||
345 | |||
346 | /** | ||
347 | * @return a new bucket consisting of a sole value | ||
348 | */ | ||
349 | abstract Bucket createSingletonBucket(Value value); | ||
350 | /** | ||
351 | * @return a read-only bucket consisting of a sole value, to be returned to the user | ||
352 | */ | ||
353 | default IMemoryView<Value> yieldSingleton(Value value) { | ||
354 | return new SingletonMemoryView<>(value); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * @param positive the previously existing value, or null if the delta is to contain a single negative tuple | ||
359 | * @return a new bucket consisting of a delta of two values | ||
360 | * @throws IllegalStateException if deltas not supported | ||
361 | */ | ||
362 | abstract Bucket createDeltaBucket(Value positive, Value negative); | ||
363 | |||
364 | /** | ||
365 | * A multi-lookup whose buckets are sets. | ||
366 | * | ||
367 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
368 | * Realized as an interface with default implementations, instead of an abstract class, | ||
369 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
370 | * | ||
371 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
372 | * and bind the lowLevel* methods accordingly. | ||
373 | * | ||
374 | * @noreference This interface is not intended to be referenced by clients. | ||
375 | * @noimplement This interface is not intended to be implemented by clients. | ||
376 | * @author Gabor Bergmann | ||
377 | */ | ||
378 | public static interface ToSetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedSet<Value>> { | ||
379 | /** | ||
380 | * @return a fresh, empty marked set | ||
381 | */ | ||
382 | public MarkedSet<Value> createMarkedSet(); | ||
383 | |||
384 | @Override | ||
385 | public default boolean negativesAllowed() { | ||
386 | return false; | ||
387 | } | ||
388 | @Override | ||
389 | default boolean duplicatesAllowed() { | ||
390 | return false; | ||
391 | } | ||
392 | |||
393 | @Override | ||
394 | public default boolean addToBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) { | ||
395 | if (bucket.addOne(value)) return true; | ||
396 | else if (throwIfImpossible) throw new IllegalStateException(); | ||
397 | else return false; | ||
398 | } | ||
399 | |||
400 | @Override | ||
401 | public default boolean removeFromBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) { | ||
402 | return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); | ||
403 | } | ||
404 | |||
405 | @Override | ||
406 | public default Value asSingleton(MarkedSet<Value> bucket) { | ||
407 | return bucket.size() == 1 ? bucket.iterator().next() : null; | ||
408 | } | ||
409 | |||
410 | @Override | ||
411 | public default MarkedSet<Value> createSingletonBucket(Value value) { | ||
412 | MarkedSet<Value> result = createMarkedSet(); | ||
413 | result.addOne(value); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | @Override | ||
418 | public default MarkedSet<Value> createDeltaBucket(Value positive, Value negative) { | ||
419 | throw new IllegalStateException(); | ||
420 | } | ||
421 | } | ||
422 | |||
423 | /** | ||
424 | * A multi-lookup whose buckets are multisets. | ||
425 | * | ||
426 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
427 | * Realized as an interface with default implementations, instead of an abstract class, | ||
428 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
429 | * | ||
430 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
431 | * and bind the lowLevel* methods accordingly. | ||
432 | * | ||
433 | * @noreference This interface is not intended to be referenced by clients. | ||
434 | * @noimplement This interface is not intended to be implemented by clients. | ||
435 | * @author Gabor Bergmann | ||
436 | */ | ||
437 | public static interface ToMultisetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedMultiset<Value>> { | ||
438 | /** | ||
439 | * @return a fresh, empty marked multiset | ||
440 | */ | ||
441 | public MarkedMemory.MarkedMultiset<Value> createMarkedMultiset(); | ||
442 | |||
443 | @Override | ||
444 | public default boolean negativesAllowed() { | ||
445 | return false; | ||
446 | } | ||
447 | @Override | ||
448 | default boolean duplicatesAllowed() { | ||
449 | return true; | ||
450 | } | ||
451 | |||
452 | @Override | ||
453 | public default boolean addToBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) { | ||
454 | return bucket.addOne(value); | ||
455 | } | ||
456 | |||
457 | @Override | ||
458 | public default boolean removeFromBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) { | ||
459 | return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); | ||
460 | } | ||
461 | |||
462 | @Override | ||
463 | public default Value asSingleton(MarkedMemory.MarkedMultiset<Value> bucket) { | ||
464 | if (bucket.size() != 1) return null; | ||
465 | Value candidate = bucket.iterator().next(); | ||
466 | return bucket.getCount(candidate) == 1 ? candidate : null; | ||
467 | } | ||
468 | |||
469 | @Override | ||
470 | public default MarkedMemory.MarkedMultiset<Value> createSingletonBucket(Value value) { | ||
471 | MarkedMemory.MarkedMultiset<Value> result = createMarkedMultiset(); | ||
472 | result.addOne(value); | ||
473 | return result; | ||
474 | } | ||
475 | |||
476 | @Override | ||
477 | public default MarkedMemory.MarkedMultiset<Value> createDeltaBucket(Value positive, Value negative) { | ||
478 | throw new IllegalStateException(); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | |||
483 | // TODO add ToDeltaBagsAbstract | ||
484 | |||
485 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java new file mode 100644 index 00000000..bdd5d597 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * An {@link IMemory} that always contains values with a nonnegative multiplicity. | ||
13 | * | ||
14 | * <p> In case a write operation caused underflow, an {@link IllegalStateException} is thrown. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public interface IMultiset<T> extends IMemory<T> { | ||
20 | |||
21 | /** | ||
22 | * Adds the given number of occurrences to the memory. The count value must be a positive number. | ||
23 | * | ||
24 | * @param count | ||
25 | * the number of occurrences | ||
26 | * @return true if the tuple was not present before in the memory | ||
27 | */ | ||
28 | boolean addPositive(T value, int count); | ||
29 | |||
30 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java new file mode 100644 index 00000000..cd25dc95 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.util; | ||
10 | |||
11 | import java.util.function.Function; | ||
12 | import java.util.function.Supplier; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * A provider interface useful in various registry instances. | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * | ||
21 | */ | ||
22 | public interface IProvider<T> extends Supplier<T>{ | ||
23 | |||
24 | public final class ProvidedValueFunction implements Function<IProvider<PQuery>, PQuery> { | ||
25 | @Override | ||
26 | public PQuery apply(IProvider<PQuery> input) { | ||
27 | return (input == null) ? null : input.get(); | ||
28 | } | ||
29 | } | ||
30 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java new file mode 100644 index 00000000..0c03da48 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | import java.util.function.BiConsumer; | ||
12 | |||
13 | /** | ||
14 | * An {@link IMemory} that always contains values with a 0 or +1 multiplicity. | ||
15 | * | ||
16 | * <p> In case a write operation causes underflow or overflow, an {@link IllegalStateException} is thrown. | ||
17 | * | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public interface ISetMemory<T> extends IMemory<T> { | ||
22 | |||
23 | @Override | ||
24 | default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
25 | for (T t : this.distinctValues()) entryConsumer.accept(t, 1); | ||
26 | } | ||
27 | |||
28 | |||
29 | @Override | ||
30 | default boolean removeOne(T value) { | ||
31 | if (!removeOneOrNop(value)) | ||
32 | throw new IllegalStateException(); | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java new file mode 100644 index 00000000..3be078bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java | |||
@@ -0,0 +1,102 @@ | |||
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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.BiConsumer; | ||
16 | |||
17 | /** | ||
18 | * Wraps a Map<T, Integer> (mapping elements to non-zero multiplicities) into an {@link IMemoryView}. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.0 | ||
22 | */ | ||
23 | public class MapBackedMemoryView<T> implements IMemoryView<T> { | ||
24 | |||
25 | private Map<T, Integer> wrapped; | ||
26 | |||
27 | /** | ||
28 | * @param wrapped an equivalent map from contained objects to multiplicities | ||
29 | */ | ||
30 | protected MapBackedMemoryView(Map<T, Integer> wrapped) { | ||
31 | super(); | ||
32 | this.wrapped = wrapped; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Iterator<T> iterator() { | ||
37 | return wrapped.keySet().iterator(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int getCount(T value) { | ||
42 | return getCountUnsafe(value); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getCountUnsafe(Object value) { | ||
47 | Integer count = wrapped.get(value); | ||
48 | return count == null ? 0 : count; | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public boolean containsNonZero(T value) { | ||
53 | return wrapped.containsKey(value); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean containsNonZeroUnsafe(Object value) { | ||
58 | return wrapped.containsKey(value); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public int size() { | ||
63 | return wrapped.size(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public boolean isEmpty() { | ||
68 | return wrapped.isEmpty(); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Set<T> distinctValues() { | ||
73 | return wrapped.keySet(); | ||
74 | } | ||
75 | |||
76 | |||
77 | @Override | ||
78 | public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
79 | for (Entry<T, Integer> entry : wrapped.entrySet()) { | ||
80 | entryConsumer.accept(entry.getKey(), entry.getValue()); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public Iterable<Entry<T, Integer>> entriesWithMultiplicities() { | ||
86 | return wrapped.entrySet(); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public int hashCode() { | ||
91 | return IMemoryView.hashCode(this); | ||
92 | } | ||
93 | @Override | ||
94 | public boolean equals(Object obj) { | ||
95 | return IMemoryView.equals(this, obj); | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public String toString() { | ||
100 | return wrapped.toString(); | ||
101 | } | ||
102 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java new file mode 100644 index 00000000..d22dcbe7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs 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.util; | ||
10 | |||
11 | /** | ||
12 | * Internal marker type, must only be instantiated inside implementors of IMultiLookupImpl | ||
13 | * @noimplement This interface is not intended to be implemented by clients. | ||
14 | * @since 2.0 | ||
15 | */ | ||
16 | public interface MarkedMemory<Value> extends IMemory<Value> { | ||
17 | |||
18 | static interface MarkedSet<Value> extends MarkedMemory<Value> {} | ||
19 | static interface MarkedMultiset<Value> extends MarkedMemory<Value> {} | ||
20 | static interface MarkedDeltaBag<Value> extends MarkedMemory<Value> {} | ||
21 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java new file mode 100644 index 00000000..49711a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java | |||
@@ -0,0 +1,117 @@ | |||
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.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | /** | ||
18 | * A partial and read-only Map implementation, mapping elements to multiplicities backed by an {@link IMemoryView}. | ||
19 | * | ||
20 | * <p> Not implemented: write methods. | ||
21 | * | ||
22 | * <p> Inefficiently implemented: {@link #containsValue(Object)}, {@link #values()}, {@link #entrySet()}. | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | public class MemoryViewBackedMapView<T> implements Map<T, Integer> { | ||
28 | |||
29 | private static final String READ_ONLY = "Read only"; | ||
30 | private final IMemoryView<T> wrapped; | ||
31 | |||
32 | /** | ||
33 | * @param wrapped a memory view whose contents are to be exposed as an element-to-integer map. | ||
34 | */ | ||
35 | protected MemoryViewBackedMapView(IMemoryView<T> wrapped) { | ||
36 | super(); | ||
37 | this.wrapped = wrapped; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int size() { | ||
42 | return wrapped.size(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public boolean isEmpty() { | ||
47 | return wrapped.isEmpty(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean containsKey(Object key) { | ||
52 | return wrapped.containsNonZeroUnsafe(key); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean containsValue(Object value) { | ||
57 | if (value instanceof Integer) { | ||
58 | for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) { | ||
59 | if (entry.getValue().equals(value)) return true; | ||
60 | } | ||
61 | } | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public Integer put(T key, Integer value) { | ||
67 | throw new UnsupportedOperationException(READ_ONLY); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public Integer get(Object key) { | ||
72 | int count = wrapped.getCountUnsafe(key); | ||
73 | if (count == 0) return null; else return count; | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public Integer remove(Object key) { | ||
78 | throw new UnsupportedOperationException(READ_ONLY); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public void putAll(Map<? extends T, ? extends Integer> m) { | ||
83 | throw new UnsupportedOperationException(READ_ONLY); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void clear() { | ||
88 | throw new UnsupportedOperationException(READ_ONLY); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public Set<T> keySet() { | ||
93 | return wrapped.distinctValues(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Collection<Integer> values() { | ||
98 | Collection<Integer> result = new ArrayList<>(); | ||
99 | wrapped.forEachEntryWithMultiplicities((value, count) -> result.add(count)); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Set<Entry<T, Integer>> entrySet() { | ||
105 | Set<Entry<T, Integer>> result = new HashSet<>(); | ||
106 | for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) { | ||
107 | result.add(entry); | ||
108 | } | ||
109 | return result; | ||
110 | } | ||
111 | |||
112 | |||
113 | @Override | ||
114 | public String toString() { | ||
115 | return wrapped.toString(); | ||
116 | } | ||
117 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java new file mode 100644 index 00000000..e9e5e3a0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java | |||
@@ -0,0 +1,208 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, 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.util; | ||
10 | |||
11 | import java.util.function.Supplier; | ||
12 | |||
13 | /** | ||
14 | * This class was motivated by the similar Preconditions class from Guava to provide simple precondition checking | ||
15 | * functionality. However, as starting with version 2.0 the runtime of VIATRA Query should not depend on Guava, the | ||
16 | * relevant functionality of the Preconditions checking functionality will be implemented here. | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * @since 2.0 | ||
20 | * | ||
21 | */ | ||
22 | public final class Preconditions { | ||
23 | |||
24 | private Preconditions() { | ||
25 | /* Utility class constructor */ } | ||
26 | |||
27 | /** | ||
28 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
29 | * | ||
30 | * @param expression | ||
31 | * a boolean expression | ||
32 | * @throws IllegalArgumentException | ||
33 | * if {@code expression} is false | ||
34 | */ | ||
35 | public static void checkArgument(boolean expression) { | ||
36 | if (!expression) { | ||
37 | throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
43 | * | ||
44 | * @param expression | ||
45 | * a boolean expression | ||
46 | * @param errorMessage | ||
47 | * the exception message to use if the check fails | ||
48 | * @throws IllegalArgumentException | ||
49 | * if {@code expression} is false | ||
50 | */ | ||
51 | public static void checkArgument(boolean expression, String errorMessage) { | ||
52 | if (!expression) { | ||
53 | throw new IllegalArgumentException(errorMessage); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
59 | * | ||
60 | * @param expression | ||
61 | * a boolean expression | ||
62 | * @param errorMessageTemplate | ||
63 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
64 | * as used by {@link String#format(String, Object...)}. | ||
65 | * @param errorMessageArgs | ||
66 | * the arguments to be substituted into the message template. | ||
67 | * @throws IllegalArgumentException | ||
68 | * if {@code expression} is false | ||
69 | * @throws NullPointerException | ||
70 | * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't | ||
71 | * let this happen) | ||
72 | */ | ||
73 | public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { | ||
74 | if (!expression) { | ||
75 | throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
81 | * | ||
82 | * @param expression | ||
83 | * a boolean expression | ||
84 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
85 | * @throws IllegalArgumentException | ||
86 | * if {@code expression} is false | ||
87 | */ | ||
88 | public static void checkArgument(boolean expression, Supplier<String> messageSupplier) { | ||
89 | if (!expression) { | ||
90 | throw new IllegalArgumentException(messageSupplier.get()); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Ensures the truth of an expression involving one or more fields of a class. | ||
96 | * | ||
97 | * @param expression | ||
98 | * a boolean expression | ||
99 | * @throws IllegalStateException | ||
100 | * if {@code expression} is false | ||
101 | */ | ||
102 | public static void checkState(boolean expression) { | ||
103 | if (!expression) { | ||
104 | throw new IllegalStateException(); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Ensures the truth of an expression involving one or more fields of a class. | ||
110 | * | ||
111 | * @param expression | ||
112 | * a boolean expression | ||
113 | * @param errorMessage | ||
114 | * the exception message to use if the check fails | ||
115 | * @throws IllegalStateException | ||
116 | * if {@code expression} is false | ||
117 | */ | ||
118 | public static void checkState(boolean expression, String errorMessage) { | ||
119 | if (!expression) { | ||
120 | throw new IllegalStateException(errorMessage); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Ensures the truth of an expression involving one or more fields of a class. | ||
126 | * | ||
127 | * @param expression | ||
128 | * a boolean expression | ||
129 | * @param errorMessageTemplate | ||
130 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
131 | * as used by {@link String#format(String, Object...)}. | ||
132 | * @param errorMessageArgs | ||
133 | * the arguments to be substituted into the message template. | ||
134 | * @throws IllegalStateException | ||
135 | * if {@code expression} is false | ||
136 | * @throws NullPointerException | ||
137 | * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't | ||
138 | * let this happen) | ||
139 | */ | ||
140 | public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { | ||
141 | if (!expression) { | ||
142 | throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Ensures the truth of an expression involving one or more fields of a class. | ||
148 | * | ||
149 | * @param expression | ||
150 | * a boolean expression | ||
151 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
152 | * @throws IllegalStateException | ||
153 | * if {@code expression} is false | ||
154 | */ | ||
155 | public static void checkState(boolean expression, Supplier<String> messageSupplier) { | ||
156 | if (!expression) { | ||
157 | throw new IllegalStateException(messageSupplier.get()); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * Ensures that an index is appropriate for a list or array of given size. | ||
163 | * | ||
164 | * @param index | ||
165 | * @param size | ||
166 | * @throws IndexOutOfBoundsException | ||
167 | * if index is negative or is greater or equal to size | ||
168 | */ | ||
169 | public static void checkElementIndex(int index, int size) { | ||
170 | if (index < 0 || index >= size) { | ||
171 | throw new IndexOutOfBoundsException(); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * Ensures that an index is appropriate for a list or array of given size. | ||
177 | * | ||
178 | * @param index | ||
179 | * @param size | ||
180 | * @param errorMessageTemplate | ||
181 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
182 | * as used by {@link String#format(String, Object...)}. | ||
183 | * @param errorMessageArgs | ||
184 | * the arguments to be substituted into the message template. | ||
185 | * @throws IndexOutOfBoundsException | ||
186 | * if index is negative or is greater or equal to size | ||
187 | */ | ||
188 | public static void checkElementIndex(int index, int size, String errorMessageTemplate, Object... errorMessageArgs) { | ||
189 | if (index < 0 || index >= size) { | ||
190 | throw new IndexOutOfBoundsException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Ensures that an index is appropriate for a list or array of given size. | ||
196 | * | ||
197 | * @param index | ||
198 | * @param size | ||
199 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
200 | * @throws IndexOutOfBoundsException | ||
201 | * if index is negative or is greater or equal to size | ||
202 | */ | ||
203 | public static void checkElementIndex(int index, int size, Supplier<String> messageSupplier) { | ||
204 | if (index < 0 || index >= size) { | ||
205 | throw new IndexOutOfBoundsException(messageSupplier.get()); | ||
206 | } | ||
207 | } | ||
208 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java new file mode 100644 index 00000000..c4e6b5af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, 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.util; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.Map; | ||
13 | import java.util.function.Supplier; | ||
14 | |||
15 | /** | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | * @noreference This class is not intended to be referenced by clients. | ||
19 | */ | ||
20 | public class PurgableCache implements ICache { | ||
21 | |||
22 | Map<Object, Object> storage = new HashMap<>(); | ||
23 | |||
24 | @Override | ||
25 | @SuppressWarnings("unchecked") | ||
26 | public <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider) { | ||
27 | if (storage.containsKey(key)) { | ||
28 | Object value = storage.get(key); | ||
29 | Preconditions.checkState(clazz.isInstance(value), "Cache stores for key %s a value of %s that is incompatible with the requested type %s", key, value, clazz); | ||
30 | return (T) value; | ||
31 | } else { | ||
32 | T value = valueProvider.get(); | ||
33 | storage.put(key, value); | ||
34 | return value; | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Removes all values stored in the cache | ||
40 | */ | ||
41 | public void purge() { | ||
42 | storage.clear(); | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java new file mode 100644 index 00000000..3749fe06 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java | |||
@@ -0,0 +1,90 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, 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 | * Contributors: | ||
10 | * Gabor Bergmann - initial API and implementation | ||
11 | *******************************************************************************/ | ||
12 | package tools.refinery.viatra.runtime.matchers.util; | ||
13 | |||
14 | import java.util.ArrayList; | ||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
20 | /** | ||
21 | * This class was motivated by the similar Sets class from Guava to provide simple set manipulation | ||
22 | * functionality. However, as starting with version 2.3 the runtime of VIATRA Query should not depend on Guava, | ||
23 | * not even internally, the relevant subset of Sets methods will be reimplemented here. | ||
24 | * | ||
25 | * <p> The current approach is to delegate to Eclipse Collections wherever possible. | ||
26 | * Such glue methods are useful so that downstream clients can avoid directly depending on Eclipse Collections. | ||
27 | * | ||
28 | * <p> Without an equivalent from Eclipse Collections, {@link #cartesianProduct(List)} is implemented here from scratch. | ||
29 | * | ||
30 | * @author Gabor Bergmann | ||
31 | * @since 2.3 | ||
32 | */ | ||
33 | public final class Sets { | ||
34 | |||
35 | /** | ||
36 | * @since 2.4 | ||
37 | */ | ||
38 | public static <A> Set<A> newSet(Iterable<A> elements) { | ||
39 | return org.eclipse.collections.impl.factory.Sets.mutable.ofAll(elements); | ||
40 | } | ||
41 | |||
42 | public static <A> Set<A> intersection(Set<A> left, Set<A> right) { | ||
43 | return org.eclipse.collections.impl.factory.Sets.intersect(left, right); | ||
44 | } | ||
45 | |||
46 | public static <A> Set<A> difference(Set<A> left, Set<A> right) { | ||
47 | return org.eclipse.collections.impl.factory.Sets.difference(left, right); | ||
48 | } | ||
49 | |||
50 | public static <A> Set<A> union(Set<A> left, Set<A> right) { | ||
51 | return org.eclipse.collections.impl.factory.Sets.union(left, right); | ||
52 | } | ||
53 | |||
54 | public static <A> Set<? extends Set<A>> powerSet(Set<A> set) { | ||
55 | return org.eclipse.collections.impl.factory.Sets.powerSet(set); | ||
56 | } | ||
57 | |||
58 | public static <A> Set<List<A>> cartesianProduct(List<? extends Set<? extends A>> setsList) { | ||
59 | |||
60 | class Suffix { // simple immutable linked list | ||
61 | private A head; | ||
62 | private Suffix next; | ||
63 | |||
64 | public Suffix(A head, Suffix next) { | ||
65 | super(); | ||
66 | this.head = head; | ||
67 | this.next = next; | ||
68 | } | ||
69 | |||
70 | public List<A> toList() { | ||
71 | ArrayList<A> result = new ArrayList<>(); | ||
72 | for (Suffix cursor = this; cursor!=null; cursor = cursor.next) | ||
73 | result.add(cursor.head); | ||
74 | return result; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // build result lists from end to start, in the form of suffixes | ||
79 | Stream<Suffix> suffixes = Stream.of((Suffix) null /* empty suffix*/); | ||
80 | for (int i = setsList.size()-1; i>=0; --i) { // iterate sets in reverse order | ||
81 | Set<? extends A> set = setsList.get(i); | ||
82 | suffixes = suffixes.flatMap(suffix -> set.stream().map(newElement -> new Suffix(newElement, suffix))); | ||
83 | } | ||
84 | |||
85 | |||
86 | return suffixes.map(Suffix::toList).collect(Collectors.toSet()); | ||
87 | } | ||
88 | |||
89 | |||
90 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java new file mode 100644 index 00000000..8f8bc228 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * A piece of data associated with a direction. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public class Signed<Payload extends Comparable<Payload>> { | ||
20 | |||
21 | private final Payload payload; | ||
22 | private final Direction direction; | ||
23 | |||
24 | public Signed(final Direction direction, final Payload payload) { | ||
25 | this.payload = payload; | ||
26 | this.direction = direction; | ||
27 | } | ||
28 | |||
29 | public Payload getPayload() { | ||
30 | return payload; | ||
31 | } | ||
32 | |||
33 | public Direction getDirection() { | ||
34 | return direction; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public int hashCode() { | ||
39 | return Objects.hash(direction, payload); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean equals(final Object obj) { | ||
44 | if (this == obj) { | ||
45 | return true; | ||
46 | } else if (obj == null || this.getClass() != obj.getClass()) { | ||
47 | return false; | ||
48 | } else { | ||
49 | @SuppressWarnings("rawtypes") | ||
50 | final Signed other = (Signed) obj; | ||
51 | return direction == other.direction && Objects.equals(payload, other.payload); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public String toString() { | ||
57 | return this.direction.asSign() + this.payload.toString(); | ||
58 | } | ||
59 | |||
60 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java new file mode 100644 index 00000000..cc5963f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * A provider implementation that always returns the same object instance. | ||
13 | * @author Zoltan Ujhelyi | ||
14 | */ | ||
15 | public class SingletonInstanceProvider<T> implements IProvider<T>{ | ||
16 | |||
17 | private T instance; | ||
18 | |||
19 | public SingletonInstanceProvider(T instance) { | ||
20 | Preconditions.checkArgument(instance != null, "Instance parameter must not be null."); | ||
21 | this.instance = instance; | ||
22 | } | ||
23 | |||
24 | @Override | ||
25 | public T get() { | ||
26 | return instance; | ||
27 | } | ||
28 | |||
29 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java new file mode 100644 index 00000000..b303f9ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java | |||
@@ -0,0 +1,105 @@ | |||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import java.util.Set; | ||
15 | |||
16 | /** | ||
17 | * An immutable memory view that consists of a single non-null element with multiplicity 1. | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public final class SingletonMemoryView<Value> implements IMemoryView<Value> { | ||
22 | |||
23 | private Value wrapped; | ||
24 | private static final int ONE_HASH = Integer.valueOf(1).hashCode(); | ||
25 | |||
26 | public SingletonMemoryView(Value value) { | ||
27 | this.wrapped = value; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Iterator<Value> iterator() { | ||
32 | return new Iterator<Value>() { | ||
33 | boolean hasNext = true; | ||
34 | |||
35 | @Override | ||
36 | public boolean hasNext() { | ||
37 | return hasNext; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Value next() { | ||
42 | if (hasNext) { | ||
43 | hasNext = false; | ||
44 | return wrapped; | ||
45 | } else throw new NoSuchElementException(); | ||
46 | } | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public int getCount(Value value) { | ||
52 | return wrapped.equals(value) ? 1 : 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public int getCountUnsafe(Object value) { | ||
57 | return wrapped.equals(value) ? 1 : 0; | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public boolean containsNonZero(Value value) { | ||
62 | return wrapped.equals(value); | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public boolean containsNonZeroUnsafe(Object value) { | ||
67 | return wrapped.equals(value); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public int size() { | ||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public boolean isEmpty() { | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Set<Value> distinctValues() { | ||
82 | return Collections.singleton(wrapped); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean equals(Object obj) { | ||
87 | if (obj instanceof IMemoryView<?>) { | ||
88 | IMemoryView<?> other = (IMemoryView<?>) obj; | ||
89 | if (1 != other.size()) return false; | ||
90 | if (1 != other.getCountUnsafe(wrapped)) return false; | ||
91 | return true; | ||
92 | } | ||
93 | return false; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public int hashCode() { | ||
98 | return wrapped.hashCode() ^ ONE_HASH; | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public String toString() { | ||
103 | return "{" + wrapped + "}"; | ||
104 | } | ||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java new file mode 100644 index 00000000..90fcad4d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java | |||
@@ -0,0 +1,517 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.NavigableMap; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; | ||
25 | |||
26 | /** | ||
27 | * A timely memory implementation that incrementally maintains the {@link Timeline}s of tuples. The memory is capable of | ||
28 | * lazy folding (see {@link Resumable}). | ||
29 | * | ||
30 | * @author Tamas Szabo | ||
31 | * @since 2.3 | ||
32 | */ | ||
33 | public class TimelyMemory<Timestamp extends Comparable<Timestamp>> implements Clearable, UnmaskedResumable<Timestamp> { | ||
34 | |||
35 | protected final Map<Tuple, TreeMap<Timestamp, CumulativeCounter>> counters; | ||
36 | protected final Map<Tuple, Timeline<Timestamp>> timelines; | ||
37 | public final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState; | ||
38 | protected final Set<Tuple> presentAtInfinity; | ||
39 | protected final boolean isLazy; | ||
40 | protected final Diff<Timestamp> EMPTY_DIFF = new Diff<Timestamp>(); | ||
41 | |||
42 | public TimelyMemory() { | ||
43 | this(false); | ||
44 | } | ||
45 | |||
46 | public TimelyMemory(final boolean isLazy) { | ||
47 | this.counters = CollectionsFactory.createMap(); | ||
48 | this.timelines = CollectionsFactory.createMap(); | ||
49 | this.presentAtInfinity = CollectionsFactory.createSet(); | ||
50 | this.isLazy = isLazy; | ||
51 | if (isLazy) { | ||
52 | this.foldingState = CollectionsFactory.createTreeMap(); | ||
53 | } else { | ||
54 | this.foldingState = null; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Set<Tuple> getResumableTuples() { | ||
60 | if (this.foldingState == null || this.foldingState.isEmpty()) { | ||
61 | return Collections.emptySet(); | ||
62 | } else { | ||
63 | return this.foldingState.firstEntry().getValue().keySet(); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Timestamp getResumableTimestamp() { | ||
69 | if (this.foldingState == null || this.foldingState.isEmpty()) { | ||
70 | return null; | ||
71 | } else { | ||
72 | return this.foldingState.firstKey(); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the | ||
78 | * two states will be merged together. | ||
79 | */ | ||
80 | protected void addFoldingState(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { | ||
81 | assert state.diff != 0; | ||
82 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp, | ||
83 | k -> CollectionsFactory.createMap()); | ||
84 | tupleMap.compute(tuple, (k, v) -> { | ||
85 | return v == null ? state : v.merge(state); | ||
86 | }); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp) { | ||
91 | Timestamp current = this.getResumableTimestamp(); | ||
92 | if (current == null) { | ||
93 | throw new IllegalStateException("There is othing to fold!"); | ||
94 | } else if (current.compareTo(timestamp) != 0) { | ||
95 | // It can happen that already registered folding states end up having zero diffs, | ||
96 | // and we are instructed to continue folding at a timestamp that is higher | ||
97 | // than the lowest timestamp with a folding state. | ||
98 | // However, we only do garbage collection in doFoldingState, so now it is time to | ||
99 | // first clean up those states with zero diffs. | ||
100 | while (current != null && current.compareTo(timestamp) < 0) { | ||
101 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(current); | ||
102 | for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) { | ||
103 | final Tuple key = entry.getKey(); | ||
104 | final FoldingState value = entry.getValue(); | ||
105 | if (value.diff != 0) { | ||
106 | throw new IllegalStateException("Expected zero diff during garbage collection at " + current | ||
107 | + ", but the diff was " + value.diff + "!"); | ||
108 | } | ||
109 | doFoldingStep(key, value, current); | ||
110 | } | ||
111 | current = this.getResumableTimestamp(); | ||
112 | } | ||
113 | if (current == null || current.compareTo(timestamp) != 0) { | ||
114 | throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | final Map<Tuple, Diff<Timestamp>> diffMap = CollectionsFactory.createMap(); | ||
119 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp); | ||
120 | for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) { | ||
121 | final Tuple key = entry.getKey(); | ||
122 | final FoldingState value = entry.getValue(); | ||
123 | diffMap.put(key, doFoldingStep(key, value, timestamp)); | ||
124 | } | ||
125 | |||
126 | if (this.foldingState.get(timestamp) != null) { | ||
127 | throw new IllegalStateException( | ||
128 | "Folding at " + timestamp + " produced more folding work at the same timestamp!"); | ||
129 | } | ||
130 | |||
131 | return diffMap; | ||
132 | } | ||
133 | |||
134 | protected Diff<Timestamp> doFoldingStep(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { | ||
135 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
136 | if (state.diff == 0) { | ||
137 | gcCounters(counter, tuple, timestamp); | ||
138 | return EMPTY_DIFF; | ||
139 | } else { | ||
140 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
141 | final Timestamp nextTimestamp = this.counters.get(tuple).higherKey(timestamp); | ||
142 | |||
143 | final int oldCumulative = counter.cumulative; | ||
144 | |||
145 | counter.cumulative += state.diff; | ||
146 | |||
147 | computeDiffsLazy(state.diff < 0 ? Direction.DELETE : Direction.INSERT, oldCumulative, counter.cumulative, | ||
148 | timestamp, nextTimestamp, resultDiff); | ||
149 | |||
150 | gcCounters(counter, tuple, timestamp); | ||
151 | updateTimeline(tuple, resultDiff); | ||
152 | |||
153 | // prepare folding state for next timestamp | ||
154 | if (nextTimestamp != null) { | ||
155 | // propagate the incoming diff, not the diff stored in counter | ||
156 | addFoldingState(tuple, new FoldingState(state.diff), nextTimestamp); | ||
157 | } | ||
158 | |||
159 | return resultDiff; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * On-demand initializes and returns the counter for the given tuple and timestamp. | ||
165 | */ | ||
166 | protected CumulativeCounter getCounter(final Tuple tuple, final Timestamp timestamp) { | ||
167 | final TreeMap<Timestamp, CumulativeCounter> counterTimeline = this.counters.computeIfAbsent(tuple, | ||
168 | k -> CollectionsFactory.createTreeMap()); | ||
169 | |||
170 | final CumulativeCounter counter = counterTimeline.computeIfAbsent(timestamp, k -> { | ||
171 | final Entry<Timestamp, CumulativeCounter> previousCounter = counterTimeline.lowerEntry(k); | ||
172 | final int previousCumulative = previousCounter == null ? 0 : previousCounter.getValue().cumulative; | ||
173 | return new CumulativeCounter(0, previousCumulative); | ||
174 | }); | ||
175 | |||
176 | return counter; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Garbage collects the counter of the given tuple and timestamp if the new diff is zero. | ||
181 | */ | ||
182 | protected void gcCounters(final CumulativeCounter counter, final Tuple tuple, final Timestamp timestamp) { | ||
183 | if (counter.diff == 0) { | ||
184 | final TreeMap<Timestamp, CumulativeCounter> counterMap = this.counters.get(tuple); | ||
185 | counterMap.remove(timestamp); | ||
186 | if (counterMap.isEmpty()) { | ||
187 | this.counters.remove(tuple); | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * Utility method that computes the timeline diffs in case of lazy memories. The diffs will be inserted into the | ||
194 | * input parameter. This method computes diffs for entire plateaus that spans from timestamp to nextTimestamp. | ||
195 | * | ||
196 | * Compared to the eager version of this method, the lazy version makes use of both the old and the new cumulative | ||
197 | * values because it can happen that the cumulative is incremented by a value that is larger than 1 (as folding | ||
198 | * states are merged together). This means that we cant decide whether the cumulative became positive by comparing | ||
199 | * the new value to 1. | ||
200 | */ | ||
201 | protected void computeDiffsLazy(final Direction direction, final int oldCumulative, final int newCumulative, | ||
202 | final Timestamp timestamp, final Timestamp nextTimestamp, final Diff<Timestamp> diffs) { | ||
203 | if (direction == Direction.INSERT) { | ||
204 | if (newCumulative == 0) { | ||
205 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
206 | } else { | ||
207 | if (oldCumulative == 0 /* current became positive */) { | ||
208 | // (1) either we sent out a DELETE before and now we need to cancel it, | ||
209 | // (2) or we just INSERT this for the first time | ||
210 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
211 | if (nextTimestamp != null) { | ||
212 | diffs.add(new Signed<>(Direction.DELETE, nextTimestamp)); | ||
213 | } | ||
214 | } else /* current stays positive */ { | ||
215 | // nothing to do | ||
216 | } | ||
217 | } | ||
218 | } else { | ||
219 | if (newCumulative < 0) { | ||
220 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
221 | } else { | ||
222 | if (newCumulative == 0 /* current became zero */) { | ||
223 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
224 | if (nextTimestamp != null) { | ||
225 | diffs.add(new Signed<>(Direction.INSERT, nextTimestamp)); | ||
226 | } | ||
227 | } else /* current stays positive */ { | ||
228 | // nothing to do | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * Utility method that computes the timeline diffs in case of eager memories. The diffs will be inserted into the | ||
236 | * input parameter. This method computes diffs that describe momentary changes instead of plateaus. Returns a | ||
237 | * {@link SignChange} that describes how the sign has changed at the given timestamp. | ||
238 | */ | ||
239 | protected SignChange computeDiffsEager(final Direction direction, final CumulativeCounter counter, | ||
240 | final SignChange signChangeAtPrevious, final Timestamp timestamp, final Diff<Timestamp> diffs) { | ||
241 | if (direction == Direction.INSERT) { | ||
242 | if (counter.cumulative == 0) { | ||
243 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
244 | } else { | ||
245 | if (counter.cumulative == 1 /* current became positive */) { | ||
246 | if (signChangeAtPrevious != SignChange.BECAME_POSITIVE) { | ||
247 | // (1) either we sent out a DELETE before and now we need to cancel it, | ||
248 | // (2) or we just INSERT this for the first time | ||
249 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
250 | } else { | ||
251 | // we have already emitted this at the previous timestamp | ||
252 | // both previous and current became positive | ||
253 | throw new IllegalStateException( | ||
254 | "This would mean that the diff at current is 0 " + counter.diff); | ||
255 | } | ||
256 | |||
257 | // remember for next timestamp | ||
258 | return SignChange.BECAME_POSITIVE; | ||
259 | } else /* current stays positive */ { | ||
260 | if (signChangeAtPrevious == SignChange.BECAME_POSITIVE) { | ||
261 | // we sent out an INSERT before and now the timeline is positive already starting at previous | ||
262 | // we need to cancel the effect of this with a DELETE | ||
263 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
264 | } else { | ||
265 | // this is normal, both previous and current was positive and stays positive | ||
266 | } | ||
267 | |||
268 | // remember for next timestamp | ||
269 | return SignChange.IRRELEVANT; | ||
270 | } | ||
271 | } | ||
272 | } else { | ||
273 | if (counter.cumulative < 0) { | ||
274 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
275 | } else { | ||
276 | if (counter.cumulative == 0 /* current became zero */) { | ||
277 | if (signChangeAtPrevious != SignChange.BECAME_ZERO) { | ||
278 | // (1) either we sent out a INSERT before and now we need to cancel it, | ||
279 | // (2) or we just DELETE this for the first time | ||
280 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
281 | } else { | ||
282 | // we have already emitted this at the previous timestamp | ||
283 | // both previous and current became zero | ||
284 | throw new IllegalStateException( | ||
285 | "This would mean that the diff at current is 0 " + counter.diff); | ||
286 | } | ||
287 | |||
288 | // remember for next timestamp | ||
289 | return SignChange.BECAME_ZERO; | ||
290 | } else /* current stays positive */ { | ||
291 | if (signChangeAtPrevious == SignChange.BECAME_ZERO) { | ||
292 | // we sent out a DELETE before and now the timeline is zero already starting at previous | ||
293 | // we need to cancel the effect of this with a INSERT | ||
294 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
295 | } else { | ||
296 | // this is normal, both previous and current was positive and stays positive | ||
297 | } | ||
298 | |||
299 | // remember for next timestamp | ||
300 | return SignChange.IRRELEVANT; | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | public Diff<Timestamp> put(final Tuple tuple, final Timestamp timestamp) { | ||
307 | if (this.isLazy) { | ||
308 | return putLazy(tuple, timestamp); | ||
309 | } else { | ||
310 | return putEager(tuple, timestamp); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | public Diff<Timestamp> remove(final Tuple tuple, final Timestamp timestamp) { | ||
315 | if (this.isLazy) { | ||
316 | return removeLazy(tuple, timestamp); | ||
317 | } else { | ||
318 | return removeEager(tuple, timestamp); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | protected Diff<Timestamp> putEager(final Tuple tuple, final Timestamp timestamp) { | ||
323 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
324 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
325 | ++counter.diff; | ||
326 | |||
327 | // before the INSERT timestamp, no change at all | ||
328 | // it cannot happen that those became positive in this round | ||
329 | SignChange signChangeAtPrevious = SignChange.IRRELEVANT; | ||
330 | |||
331 | final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp, | ||
332 | true); | ||
333 | for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) { | ||
334 | final Timestamp currentTimestamp = currentEntry.getKey(); | ||
335 | final CumulativeCounter currentCounter = currentEntry.getValue(); | ||
336 | ++currentCounter.cumulative; | ||
337 | signChangeAtPrevious = computeDiffsEager(Direction.INSERT, currentCounter, signChangeAtPrevious, | ||
338 | currentTimestamp, resultDiff); | ||
339 | } | ||
340 | |||
341 | gcCounters(counter, tuple, timestamp); | ||
342 | updateTimeline(tuple, resultDiff); | ||
343 | |||
344 | return resultDiff; | ||
345 | } | ||
346 | |||
347 | protected Diff<Timestamp> putLazy(final Tuple tuple, final Timestamp timestamp) { | ||
348 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
349 | counter.diff += 1; | ||
350 | // before the INSERT timestamp, no change at all | ||
351 | // it cannot happen that those became positive in this round | ||
352 | addFoldingState(tuple, new FoldingState(+1), timestamp); | ||
353 | return EMPTY_DIFF; | ||
354 | } | ||
355 | |||
356 | protected Diff<Timestamp> removeEager(final Tuple tuple, final Timestamp timestamp) { | ||
357 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
358 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
359 | --counter.diff; | ||
360 | |||
361 | // before the DELETE timestamp, no change at all | ||
362 | // it cannot happen that those became zero in this round | ||
363 | SignChange signChangeAtPrevious = SignChange.IRRELEVANT; | ||
364 | |||
365 | final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp, | ||
366 | true); | ||
367 | for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) { | ||
368 | final Timestamp currentTimestamp = currentEntry.getKey(); | ||
369 | final CumulativeCounter currentCounter = currentEntry.getValue(); | ||
370 | --currentCounter.cumulative; | ||
371 | signChangeAtPrevious = computeDiffsEager(Direction.DELETE, currentCounter, signChangeAtPrevious, | ||
372 | currentTimestamp, resultDiff); | ||
373 | } | ||
374 | |||
375 | gcCounters(counter, tuple, timestamp); | ||
376 | updateTimeline(tuple, resultDiff); | ||
377 | |||
378 | return resultDiff; | ||
379 | } | ||
380 | |||
381 | protected Diff<Timestamp> removeLazy(final Tuple tuple, final Timestamp timestamp) { | ||
382 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
383 | counter.diff -= 1; | ||
384 | // before the DELETE timestamp, no change at all | ||
385 | // it cannot happen that those became zero in this round | ||
386 | addFoldingState(tuple, new FoldingState(-1), timestamp); | ||
387 | return EMPTY_DIFF; | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * Updates and garbage collects the timeline of the given tuple based on the given timeline diff. | ||
392 | */ | ||
393 | protected void updateTimeline(final Tuple tuple, final Diff<Timestamp> diff) { | ||
394 | if (!diff.isEmpty()) { | ||
395 | this.timelines.compute(tuple, (k, oldTimeline) -> { | ||
396 | this.presentAtInfinity.remove(tuple); | ||
397 | final Timeline<Timestamp> timeline = oldTimeline == null ? Timelines.createFrom(diff) | ||
398 | : oldTimeline.mergeAdditive(diff); | ||
399 | if (timeline.isPresentAtInfinity()) { | ||
400 | this.presentAtInfinity.add(tuple); | ||
401 | } | ||
402 | if (timeline.isEmpty()) { | ||
403 | return null; | ||
404 | } else { | ||
405 | return timeline; | ||
406 | } | ||
407 | }); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * @since 2.8 | ||
413 | */ | ||
414 | public Set<Tuple> getTuplesAtInfinity() { | ||
415 | return this.presentAtInfinity; | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * Returns the number of tuples that are present at the moment 'infinity'. | ||
420 | */ | ||
421 | public int getCountAtInfinity() { | ||
422 | return this.presentAtInfinity.size(); | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * Returns true if the given tuple is present at the moment 'infinity'. | ||
427 | */ | ||
428 | public boolean isPresentAtInfinity(final Tuple tuple) { | ||
429 | final Timeline<Timestamp> timeline = this.timelines.get(tuple); | ||
430 | if (timeline == null) { | ||
431 | return false; | ||
432 | } else { | ||
433 | return timeline.isPresentAtInfinity(); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | public boolean isEmpty() { | ||
438 | return this.counters.isEmpty(); | ||
439 | } | ||
440 | |||
441 | public int size() { | ||
442 | return this.counters.size(); | ||
443 | } | ||
444 | |||
445 | public Set<Tuple> keySet() { | ||
446 | return this.counters.keySet(); | ||
447 | } | ||
448 | |||
449 | public Map<Tuple, Timeline<Timestamp>> asMap() { | ||
450 | return this.timelines; | ||
451 | } | ||
452 | |||
453 | public Timeline<Timestamp> get(final ITuple tuple) { | ||
454 | return this.timelines.get(tuple); | ||
455 | } | ||
456 | |||
457 | @Override | ||
458 | public void clear() { | ||
459 | this.counters.clear(); | ||
460 | this.timelines.clear(); | ||
461 | if (this.foldingState != null) { | ||
462 | this.foldingState.clear(); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | public boolean containsKey(final ITuple tuple) { | ||
467 | return this.counters.containsKey(tuple); | ||
468 | } | ||
469 | |||
470 | @Override | ||
471 | public String toString() { | ||
472 | return this.counters + "\n" + this.timelines + "\n" + this.foldingState + "\n"; | ||
473 | } | ||
474 | |||
475 | protected static final class CumulativeCounter { | ||
476 | protected int diff; | ||
477 | protected int cumulative; | ||
478 | |||
479 | protected CumulativeCounter(final int diff, final int cumulative) { | ||
480 | this.diff = diff; | ||
481 | this.cumulative = cumulative; | ||
482 | } | ||
483 | |||
484 | @Override | ||
485 | public String toString() { | ||
486 | return "{diff=" + this.diff + ", cumulative=" + this.cumulative + "}"; | ||
487 | } | ||
488 | |||
489 | } | ||
490 | |||
491 | protected static final class FoldingState { | ||
492 | protected final int diff; | ||
493 | |||
494 | protected FoldingState(final int diff) { | ||
495 | this.diff = diff; | ||
496 | } | ||
497 | |||
498 | @Override | ||
499 | public String toString() { | ||
500 | return "{diff=" + this.diff + "}"; | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * The returned result will never be null, even if the resulting diff is zero. | ||
505 | */ | ||
506 | public FoldingState merge(final FoldingState that) { | ||
507 | Preconditions.checkArgument(that != null); | ||
508 | return new FoldingState(this.diff + that.diff); | ||
509 | } | ||
510 | |||
511 | } | ||
512 | |||
513 | protected enum SignChange { | ||
514 | BECAME_POSITIVE, BECAME_ZERO, IRRELEVANT; | ||
515 | } | ||
516 | |||
517 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java new file mode 100644 index 00000000..ea70e61d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
15 | |||
16 | /** | ||
17 | * A masked {@link Resumable} implementation, which maintains lazy folding per tuple signature. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public interface MaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> { | ||
23 | |||
24 | /** | ||
25 | * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to | ||
26 | * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more | ||
27 | * folding to do towards higher timestamps. | ||
28 | */ | ||
29 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp); | ||
30 | |||
31 | /** | ||
32 | * Returns the set of signatures for which lazy folding shall be resumed at the next timestamp. | ||
33 | */ | ||
34 | public Iterable<Tuple> getResumableSignatures(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java new file mode 100644 index 00000000..2861df20 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.util.resumable; | ||
10 | |||
11 | /** | ||
12 | * A resumable lazily folds its state towards higher timestamps. Folding shall be done in the increasing order of | ||
13 | * timestamps, and it shall be interrupted after each step. The resumable can then be instructed to resume the folding, | ||
14 | * one step at a time. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public interface Resumable<Timestamp extends Comparable<Timestamp>> { | ||
20 | |||
21 | /** | ||
22 | * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this | ||
23 | * resumable. | ||
24 | */ | ||
25 | public Timestamp getResumableTimestamp(); | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java new file mode 100644 index 00000000..1671940b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro | ||
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.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
15 | |||
16 | /** | ||
17 | * A unmasked {@link Resumable} implementation, which maintains lazy folding without caring about tuple signatures. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public interface UnmaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> { | ||
23 | |||
24 | /** | ||
25 | * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to | ||
26 | * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more | ||
27 | * folding to do towards higher timestamps. | ||
28 | */ | ||
29 | public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp); | ||
30 | |||
31 | /** | ||
32 | * Returns the set of tuples for which lazy folding shall be resumed at the next timestamp. | ||
33 | */ | ||
34 | public Iterable<Tuple> getResumableTuples(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java new file mode 100644 index 00000000..0532d094 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java | |||
@@ -0,0 +1,111 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
17 | |||
18 | /** | ||
19 | * A compact timeline may cosist of an arbitrary amount of moments. | ||
20 | * It is backed by an {@link ArrayList}. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | * @since 2.4 | ||
24 | */ | ||
25 | public class CompactTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> { | ||
26 | |||
27 | protected final List<Timestamp> elements; | ||
28 | |||
29 | CompactTimeline() { | ||
30 | this.elements = new ArrayList<>(); | ||
31 | } | ||
32 | |||
33 | CompactTimeline(final Timestamp timestamp) { | ||
34 | this(); | ||
35 | this.elements.add(timestamp); | ||
36 | } | ||
37 | |||
38 | CompactTimeline(final List<Timestamp> timestamps) { | ||
39 | this.elements = new ArrayList<>(timestamps.size()); | ||
40 | this.elements.addAll(timestamps); | ||
41 | } | ||
42 | |||
43 | CompactTimeline(final Diff<Timestamp> diff) { | ||
44 | this.elements = new ArrayList<>(diff.size()); | ||
45 | Direction expected = Direction.INSERT; | ||
46 | for (Signed<Timestamp> signed : diff) { | ||
47 | if (!expected.equals(signed.getDirection())) { | ||
48 | throw new IllegalStateException(String.format("Expected direction (%s) constraint violated! %s @%s", | ||
49 | expected, diff, signed.getPayload())); | ||
50 | } | ||
51 | this.elements.add(signed.getPayload()); | ||
52 | expected = expected.opposite(); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Signed<Timestamp> getSigned(final int index) { | ||
58 | final Direction direction = index % 2 == 0 ? Direction.INSERT : Direction.DELETE; | ||
59 | return new Signed<>(direction, this.getUnsigned(index)); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Timestamp getUnsigned(final int index) { | ||
64 | if (this.elements.size() <= index) { | ||
65 | throw new IllegalArgumentException( | ||
66 | "Timeline size (" + this.size() + ") is smaller than requested index " + index + "!"); | ||
67 | } else { | ||
68 | return this.elements.get(index); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public int size() { | ||
74 | return this.elements.size(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public boolean isPresentAtInfinity() { | ||
79 | // if it has an odd length, then it ends with "INSERT" | ||
80 | return this.size() % 2 == 1; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Iterable<Signed<Timestamp>> asChangeSequence() { | ||
85 | Iterable<Timestamp> outer = this.elements; | ||
86 | return () -> { | ||
87 | final Iterator<Timestamp> itr = outer.iterator(); | ||
88 | return new Iterator<Signed<Timestamp>>() { | ||
89 | Direction direction = Direction.INSERT; | ||
90 | |||
91 | @Override | ||
92 | public boolean hasNext() { | ||
93 | return itr.hasNext(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Signed<Timestamp> next() { | ||
98 | final Signed<Timestamp> result = new Signed<Timestamp>(direction, itr.next()); | ||
99 | direction = direction.opposite(); | ||
100 | return result; | ||
101 | } | ||
102 | }; | ||
103 | }; | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public boolean isEmpty() { | ||
108 | return this.elements.isEmpty(); | ||
109 | } | ||
110 | |||
111 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java new file mode 100644 index 00000000..cec6049e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
14 | |||
15 | /** | ||
16 | * The description of a delta that specifies how a {@link Timeline} changes. It consists of {@link Signed} timestamps that | ||
17 | * depict the moments of insertions and deletions on the timeline. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | * @param <Timestamp> | ||
22 | * the type representing the timestamps | ||
23 | */ | ||
24 | public class Diff<Timestamp extends Comparable<Timestamp>> extends ArrayList<Signed<Timestamp>> { | ||
25 | |||
26 | private static final long serialVersionUID = 3853460426655994160L; | ||
27 | |||
28 | public Diff() { | ||
29 | |||
30 | } | ||
31 | |||
32 | public void appendWithCancellation(Signed<Timestamp> item) { | ||
33 | if (this.isEmpty()) { | ||
34 | this.add(item); | ||
35 | } else { | ||
36 | final Signed<Timestamp> last = this.get(this.size() - 1); | ||
37 | final int lastMinusItem = last.getPayload().compareTo(item.getPayload()); | ||
38 | if (lastMinusItem == 0) { | ||
39 | if (last.getDirection() != item.getDirection()) { | ||
40 | // cancellation | ||
41 | this.remove(this.size() - 1); | ||
42 | } else { | ||
43 | throw new IllegalStateException( | ||
44 | "Trying to insert or delete for the second time at the same timestamp! " + item); | ||
45 | } | ||
46 | } else if (lastMinusItem > 0) { | ||
47 | throw new IllegalStateException( | ||
48 | "Trying to append a timestamp that is smaller than the last one! " + last + " " + item); | ||
49 | } else { | ||
50 | this.add(item); | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java new file mode 100644 index 00000000..526a95f5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util.timeline; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
15 | |||
16 | /** | ||
17 | * A timeline which solely consists of one timestamp value, representing a single insertion. Intuitively, a singleton | ||
18 | * timeline always represents a bump which starts at the given timestamp and lasts till plus infinity. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.4 | ||
22 | */ | ||
23 | public class SingletonTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> { | ||
24 | |||
25 | protected final Timestamp start; | ||
26 | |||
27 | SingletonTimeline(final Timestamp timestamp) { | ||
28 | this.start = timestamp; | ||
29 | } | ||
30 | |||
31 | SingletonTimeline(final Diff<Timestamp> diff) { | ||
32 | if (diff.size() != 1 || diff.get(0).getDirection() == Direction.DELETE) { | ||
33 | throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); | ||
34 | } else { | ||
35 | this.start = diff.get(0).getPayload(); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Signed<Timestamp> getSigned(final int index) { | ||
41 | return new Signed<>(Direction.INSERT, this.getUnsigned(index)); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Timestamp getUnsigned(final int index) { | ||
46 | if (index != 0) { | ||
47 | throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); | ||
48 | } else { | ||
49 | return this.start; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public int size() { | ||
55 | return 1; | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public boolean isPresentAtInfinity() { | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Iterable<Signed<Timestamp>> asChangeSequence() { | ||
65 | return Collections.singletonList(this.getSigned(0)); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public boolean isEmpty() { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java new file mode 100644 index 00000000..9214536c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java | |||
@@ -0,0 +1,146 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
17 | |||
18 | /** | ||
19 | * A timeline describes the life cycle of a piece of data (typically a tuple in a relation) as a sequence of moments. | ||
20 | * Even moments represent appearances, odd moments represent disappearances. A timeline is immutable, once created, it | ||
21 | * is not possible to extend it with further moments. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * @since 2.4 | ||
25 | */ | ||
26 | public abstract class Timeline<Timestamp extends Comparable<Timestamp>> { | ||
27 | |||
28 | public abstract Iterable<Signed<Timestamp>> asChangeSequence(); | ||
29 | |||
30 | public abstract boolean isPresentAtInfinity(); | ||
31 | |||
32 | public abstract boolean isEmpty(); | ||
33 | |||
34 | public abstract int size(); | ||
35 | |||
36 | public abstract Signed<Timestamp> getSigned(final int index); | ||
37 | |||
38 | public abstract Timestamp getUnsigned(final int index); | ||
39 | |||
40 | public Timeline<Timestamp> mergeMultiplicative(final Timeline<Timestamp> that) { | ||
41 | final List<Timestamp> result = new ArrayList<>(); | ||
42 | int thisIdx = 0, thatIdx = 0; | ||
43 | Timestamp thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
44 | Timestamp thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
45 | |||
46 | while (thisNext != null || thatNext != null) { | ||
47 | int thisMinusThat = 0; | ||
48 | if (thisNext != null && thatNext != null) { | ||
49 | thisMinusThat = thisNext.compareTo(thatNext); | ||
50 | } | ||
51 | if (thisNext == null || thisMinusThat > 0) { | ||
52 | if (thisIdx % 2 == 1) { | ||
53 | result.add(thatNext); | ||
54 | } | ||
55 | thatIdx++; | ||
56 | thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
57 | } else if (thatNext == null || thisMinusThat < 0) { | ||
58 | if (thatIdx % 2 == 1) { | ||
59 | result.add(thisNext); | ||
60 | } | ||
61 | thisIdx++; | ||
62 | thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
63 | } else { | ||
64 | if (thisIdx % 2 == thatIdx % 2) { | ||
65 | result.add(thisNext); | ||
66 | } | ||
67 | thisIdx++; | ||
68 | thatIdx++; | ||
69 | thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
70 | thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return Timelines.createFrom(result); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Merges this timeline with the given timestamp diff. The expectation is that the resulting timeline starts with an | ||
79 | * insertion. The logic is similar to a merge sort; we iterate side-by-side over the timeline and the diff. During | ||
80 | * the merge, cancellation can happen if at the same timestamp we observe different signs at the corresponding | ||
81 | * timeline and diff elements. | ||
82 | */ | ||
83 | public Timeline<Timestamp> mergeAdditive(final Diff<Timestamp> diff) { | ||
84 | final Iterator<Signed<Timestamp>> thisItr = this.asChangeSequence().iterator(); | ||
85 | final Iterator<Signed<Timestamp>> diffItr = diff.iterator(); | ||
86 | final List<Timestamp> result = new ArrayList<>(); | ||
87 | Direction expected = Direction.INSERT; | ||
88 | Signed<Timestamp> thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
89 | Signed<Timestamp> diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
90 | |||
91 | while (thisNext != null || diffNext != null) { | ||
92 | int thisMinusDiff = 0; | ||
93 | if (thisNext != null && diffNext != null) { | ||
94 | thisMinusDiff = thisNext.getPayload().compareTo(diffNext.getPayload()); | ||
95 | } | ||
96 | |||
97 | if (thisNext == null || thisMinusDiff > 0) { | ||
98 | if (!expected.equals(diffNext.getDirection())) { | ||
99 | throw new IllegalStateException( | ||
100 | String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, | ||
101 | diff, diffNext.getPayload())); | ||
102 | } | ||
103 | result.add(diffNext.getPayload()); | ||
104 | diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
105 | expected = expected.opposite(); | ||
106 | } else if (diffNext == null || thisMinusDiff < 0) { | ||
107 | if (!expected.equals(thisNext.getDirection())) { | ||
108 | throw new IllegalStateException( | ||
109 | String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, | ||
110 | diff, thisNext.getPayload())); | ||
111 | } | ||
112 | result.add(thisNext.getPayload()); | ||
113 | thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
114 | expected = expected.opposite(); | ||
115 | } else { | ||
116 | // they cancel out each other | ||
117 | if (diffNext.getDirection().equals(thisNext.getDirection())) { | ||
118 | throw new IllegalStateException(String.format("Changes do not cancel out each other! %s %s @%s", | ||
119 | this, diff, thisNext.getPayload())); | ||
120 | } | ||
121 | diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
122 | thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return Timelines.createFrom(result); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public String toString() { | ||
131 | final StringBuilder builder = new StringBuilder(); | ||
132 | builder.append("["); | ||
133 | boolean first = true; | ||
134 | for (final Signed<Timestamp> element : this.asChangeSequence()) { | ||
135 | if (first) { | ||
136 | first = false; | ||
137 | } else { | ||
138 | builder.append(", "); | ||
139 | } | ||
140 | builder.append(element.toString()); | ||
141 | } | ||
142 | builder.append("]"); | ||
143 | return builder.toString(); | ||
144 | } | ||
145 | |||
146 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java new file mode 100644 index 00000000..747fda15 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, 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.util.timeline; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * Utility class for creating {@link Timeline}s. | ||
15 | * @author Tamas Szabo | ||
16 | * @since 2.4 | ||
17 | */ | ||
18 | public final class Timelines<Timestamp extends Comparable<Timestamp>> { | ||
19 | |||
20 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createEmpty() { | ||
21 | return new CompactTimeline<Timestamp>(); | ||
22 | } | ||
23 | |||
24 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom( | ||
25 | final Diff<Timestamp> diffs) { | ||
26 | if (diffs.size() == 1) { | ||
27 | return new SingletonTimeline<Timestamp>(diffs); | ||
28 | } else { | ||
29 | return new CompactTimeline<Timestamp>(diffs); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom( | ||
34 | final List<Timestamp> timestamps) { | ||
35 | if (timestamps.size() == 1) { | ||
36 | return new SingletonTimeline<Timestamp>(timestamps.get(0)); | ||
37 | } else { | ||
38 | return new CompactTimeline<Timestamp>(timestamps); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom(final Timestamp timestamp) { | ||
43 | return new SingletonTimeline<Timestamp>(timestamp); | ||
44 | } | ||
45 | |||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/util/ViatraQueryLoggingUtil.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/util/ViatraQueryLoggingUtil.java new file mode 100644 index 00000000..229801f8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/util/ViatraQueryLoggingUtil.java | |||
@@ -0,0 +1,72 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
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.util; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
13 | |||
14 | /** | ||
15 | * Centralized logger of the VIATRA Query runtime. | ||
16 | * @author Bergmann Gabor | ||
17 | * | ||
18 | */ | ||
19 | public class ViatraQueryLoggingUtil { | ||
20 | |||
21 | private ViatraQueryLoggingUtil() {/*Utility class constructor*/} | ||
22 | |||
23 | private static Logger externalLogger; | ||
24 | |||
25 | public static void setExternalLogger(Logger externalLogger) { | ||
26 | Preconditions.checkArgument(externalLogger != null, "Must not set up null logger"); | ||
27 | ViatraQueryLoggingUtil.externalLogger = externalLogger; | ||
28 | } | ||
29 | /** | ||
30 | * Provides a static default logger. | ||
31 | */ | ||
32 | public static Logger getDefaultLogger() { | ||
33 | if (defaultRuntimeLogger == null) { | ||
34 | Logger parentLogger = externalLogger; | ||
35 | if (parentLogger == null) { | ||
36 | defaultRuntimeLogger = Logger.getLogger("org.eclipse.viatra"); | ||
37 | } else { | ||
38 | defaultRuntimeLogger = Logger.getLogger(parentLogger.getName() + ".runtime"); | ||
39 | } | ||
40 | if (defaultRuntimeLogger == null) | ||
41 | throw new AssertionError("Configuration error: unable to create default VIATRA Query runtime logger."); | ||
42 | } | ||
43 | |||
44 | return defaultRuntimeLogger; | ||
45 | } | ||
46 | |||
47 | private static String getLoggerClassname(Class<?> clazz) { | ||
48 | return clazz.getName().startsWith(getDefaultLogger().getName()) | ||
49 | ? clazz.getName() | ||
50 | : getDefaultLogger().getName() + "." + clazz.getName(); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * Provides a class-specific logger that also stores the global logger settings of the VIATRA Query runtime | ||
55 | * @param clazz | ||
56 | */ | ||
57 | public static Logger getLogger(Class<?> clazz) { | ||
58 | return Logger.getLogger(getLoggerClassname(clazz)); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Provides a named logger that also stores the global logger settings of the VIATRA Query runtime | ||
63 | * @param clazz | ||
64 | * @param name a non-empty name to append to the class names | ||
65 | * @since 2.5 | ||
66 | */ | ||
67 | public static Logger getLogger(Class<?> clazz, String name) { | ||
68 | return Logger.getLogger(getLoggerClassname(clazz) + '.' + name); | ||
69 | } | ||
70 | |||
71 | private static Logger defaultRuntimeLogger; | ||
72 | } | ||