aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java
blob: 32a3430ddbcacc6dcbfb31c24f45d53d4f2a4986 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*******************************************************************************
 * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-v20.html.
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package tools.refinery.viatra.runtime.api;

import tools.refinery.viatra.runtime.api.scope.QueryScope;
import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl;
import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
import tools.refinery.viatra.runtime.matchers.backend.*;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;

/**
 * Advanced interface to a VIATRA incremental evaluation engine.
 *
 * <p>
 * You can create a new, private, unmanaged {@link AdvancedViatraQueryEngine} instance using
 * {@link #createUnmanagedEngine(QueryScope)}. Additionally, you can access the advanced interface on any
 * {@link ViatraQueryEngine} by {@link AdvancedViatraQueryEngine#from(ViatraQueryEngine)}.
 *
 * <p>
 * While the default interface {@link ViatraQueryEngine}, is suitable for most users, this advanced interface provides more
 * control over the engine. The most important added functionality is the following:
 * <ul>
 * <li>You can have tighter control over the lifecycle of the engine, if you create a private, unmanaged engine
 * instance. For instance, a (non-managed) engine can be disposed in order to detach from the EMF model and stop
 * listening on update notifications. The indexes built previously in the engine can then be garbage collected, even if
 * the model itself is retained. Total lifecycle control is only available for private, unmanaged engines (created using
 * {@link #createUnmanagedEngine(QueryScope)}); a managed engine (obtained via {@link ViatraQueryEngine#on(QueryScope)}) is
 * shared among clients and can not be disposed or wiped.
 * <li>You can add and remove listeners to receive notification when the model or the match sets change.
 * <li>You can add and remove listeners to receive notification on engine lifecycle events, such as creation of new
 * matchers. For instance, if you explicitly share a private, unmanaged engine between multiple sites, you should
 * register a callback using {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client
 * has called the destructive methods {@link #dispose()} or {@link #wipe()}.
 * </ul>
 *
 * @author Bergmann Gabor
 * @noextend This class is not intended to be subclassed by clients.
 */
public abstract class AdvancedViatraQueryEngine extends ViatraQueryEngine {

    /**
     * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}.
     *
     * <p> Repeated invocations will return different instances, so other clients are unable to independently access
     * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements
     * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed
     * engine instance.
     *
     * <p>
     * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface
     * {@link AdvancedViatraQueryEngine}.
     *
     * <p>
     * The match set of any patterns will be incrementally refreshed upon updates from this scope.
     *
     * @param scope
     * 		the scope of query evaluation; the definition of the set of model elements that this engine is operates on.
     * 		Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model.
     * @return the advanced interface to a newly created unmanaged engine
     * @since 0.9
     */
    public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope) {
        return new ViatraQueryEngineImpl(null, scope);
    }

    /**
     * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}.
     *
     * <p> Repeated invocations will return different instances, so other clients are unable to independently access
     * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements
     * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed
     * engine instance.
     *
     * <p>
     * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface
     * {@link AdvancedViatraQueryEngine}.
     *
     * <p>
     * The match set of any patterns will be incrementally refreshed upon updates from this scope.
     *
     * @param scope
     *      the scope of query evaluation; the definition of the set of model elements that this engine is operates on.
     *      Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model.
     * @return the advanced interface to a newly created unmanaged engine
     * @since 1.4
     */
    public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope, ViatraQueryEngineOptions options) {
        return new ViatraQueryEngineImpl(null, scope, options);
    }

    /**
     * Provides access to a given existing engine through the advanced interface.
     *
     * <p>
     * Caveat: if the referenced engine is managed (i.e. created via {@link ViatraQueryEngine#on(QueryScope)}), the advanced
     * methods {@link #dispose()} and {@link #wipe()} will not be allowed.
     *
     * @param engine
     *            the engine to access using the advanced interface
     * @return a reference to the same engine conforming to the advanced interface
     */
    public static AdvancedViatraQueryEngine from(ViatraQueryEngine engine) {
        return (AdvancedViatraQueryEngine) engine;
    }

    /**
     * Add an engine lifecycle listener to this engine instance.
     *
     * @param listener
     *            the {@link ViatraQueryEngineLifecycleListener} that should listen to lifecycle events from this engine
     */
    public abstract void addLifecycleListener(ViatraQueryEngineLifecycleListener listener);

    /**
     * Remove an existing lifecycle listener from this engine instance.
     *
     * @param listener
     *            the {@link ViatraQueryEngineLifecycleListener} that should not listen to lifecycle events from this
     *            engine anymore
     */
    public abstract void removeLifecycleListener(ViatraQueryEngineLifecycleListener listener);

    /**
     * Add an model update event listener to this engine instance (that fires its callbacks according to its
     * notification level).
     *
     * @param listener
     *            the {@link ViatraQueryModelUpdateListener} that should listen to model update events from this engine.
     */
    public abstract void addModelUpdateListener(ViatraQueryModelUpdateListener listener);

    /**
     * Remove an existing model update event listener to this engine instance.
     *
     * @param listener
     *            the {@link ViatraQueryModelUpdateListener} that should not listen to model update events from this engine
     *            anymore
     */
    public abstract void removeModelUpdateListener(ViatraQueryModelUpdateListener listener);

    /**
     * Registers low-level callbacks for match appearance and disappearance on this pattern matcher.
     *
     * <p>
     * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a
     * consistent state yet. Importantly, no model modification permitted during the callback. Most users should use the
     * databinding support ({@link org.eclipse.viatra.addon.databinding.runtime.api.ViatraObservables ViatraObservables}) or the event-driven API
     * ({@link org.eclipse.viatra.transformation.evm.api.EventDrivenVM EventDrivenVM}) instead.
     *
     * <p>
     * Performance note: expected to be much more efficient than polling at {@link #addCallbackAfterUpdates(Runnable)},
     * but prone to "signal hazards", e.g. spurious match appearances that will disappear immediately afterwards.
     *
     * <p>
     * The callback can be unregistered via {@link #removeCallbackOnMatchUpdate(IMatchUpdateListener)}.
     *
     * @param fireNow
     *            if true, appearCallback will be immediately invoked on all current matches as a one-time effect. See
     *            also {@link ViatraQueryMatcher#forEachMatch(IMatchProcessor)}.
     * @param listener
     *            the listener that will be notified of each new match that appears or disappears, starting from now.
     * @param matcher
     *            the {@link ViatraQueryMatcher} for which this listener should be active
     */
    public abstract <Match extends IPatternMatch> void addMatchUpdateListener(ViatraQueryMatcher<Match> matcher,
            IMatchUpdateListener<? super Match> listener, boolean fireNow);

    /**
     * Remove an existing match update event listener to this engine instance.
     *
     * @param matcher
     *            the {@link ViatraQueryMatcher} for which this listener should not be active anymore
     * @param listener
     *            the {@link IMatchUpdateListener} that should not receive the callbacks anymore
     */
    public abstract <Match extends IPatternMatch> void removeMatchUpdateListener(ViatraQueryMatcher<Match> matcher,
            IMatchUpdateListener<? super Match> listener);


    /**
     * Access a pattern matcher based on a {@link IQuerySpecification}, overriding some of the default query evaluation hints.
     * Multiple calls may return the same matcher depending on the actual evaluation hints.
     *
     * <p> It is guaranteed that this method will always return a matcher instance which is functionally compatible
     *   with the requested functionality (see {@link IMatcherCapability}).
     *   Otherwise, the query evaluator is free to ignore any hints.
     *
     * <p> For stateful query backends (Rete), hints may be effective only the first time a matcher is created.
     * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query
     * @return a pattern matcher corresponding to the specification
     * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with the query
     * @throws ViatraQueryRuntimeException if the matcher could not be initialized
     * @since 0.9
     */
    public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher(
            IQuerySpecification<Matcher> querySpecification,
            QueryEvaluationHint optionalEvaluationHints);

    /**
     * Initializes matchers for a group of patterns as one step (optionally overriding some of the default query evaluation hints).
     * If some of the pattern matchers are already
     * constructed in the engine, no task is performed for them.
     *
     * <p>
     * This preparation step has the advantage that it prepares pattern matchers for an arbitrary number of patterns in a
     * single-pass traversal of the model.
     * This is typically more efficient than traversing the model each time an individual pattern matcher is initialized on demand.
     * The performance benefit only manifests itself if the engine is not in wildcard mode.
     *
     * @param queryGroup a {@link IQueryGroup} identifying a set of VIATRA queries
     * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with each query
     * @throws ViatraQueryRuntimeException
     *             if there was an error in preparing the engine
     * @since 0.9
     */
    public abstract void prepareGroup(IQueryGroup queryGroup, QueryEvaluationHint optionalEvaluationHints);

    /**
     * Indicates whether the engine is managed, i.e. the default engine assigned to the given scope root by
     * {@link ViatraQueryEngine#on(QueryScope)}.
     *
     * <p>
     * If the engine is managed, there may be other clients using it, as all calls to
     * {@link ViatraQueryEngine#on(QueryScope)} return the same managed engine instance for a given scope root. Therefore the
     * destructive methods {@link #wipe()} and {@link #dispose()} are not allowed.
     *
     * <p>
     * On the other hand, if the engine is unmanaged (i.e. a private instance created using
     * {@link #createUnmanagedEngine(QueryScope)}), then {@link #wipe()} and {@link #dispose()} can be called. If you
     * explicitly share a private, unmanaged engine between multiple sites, register a callback using
     * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called these
     * destructive methods.
     *
     * @return true if the engine is managed, and therefore potentially shared with other clients querying the same EMF
     *         model
     */
    public abstract boolean isManaged();

    /**
     * Indicates whether the engine is in a tainted, inconsistent state due to some internal errors. If true, results
     * are no longer reliable; engine should be disposed.
     *
     * <p>
     * The engine is in a tainted state if any of its internal processes report back a fatal error. The
     * {@link ViatraQueryEngineLifecycleListener} interface provides a callback method for entering the tainted state.
     *
     * @return the tainted state
     */
    public abstract boolean isTainted();

    /**
     * Discards any pattern matcher caches and forgets known patterns. The base index built directly on the underlying
     * EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. Use this method if you
     * have e.g. new versions of the same patterns, to be matched on the same model.
     *
     * <p>
     * Matcher objects will continue to return stale results. If no references are retained to the matchers, they can
     * eventually be GC'ed.
     * <p>
     * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it.
     * <p>
     * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using
     * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this
     * destructive method.
     *
     * @throws UnsupportedOperationException
     *             if engine is managed
     */
    public abstract void wipe();

    /**
     * Completely disconnects and dismantles the engine. Cannot be reversed.
     * <p>
     * Matcher objects will continue to return stale results. If no references are retained to the matchers or the
     * engine, they can eventually be GC'ed, and they won't block the EMF model from being GC'ed anymore.
     * <p>
     * The base indexer (see {@link #getBaseIndex()}) built on the model will be disposed alongside the engine, unless
     * the user has manually added listeners on the base index that were not removed yet.
     * <p>
     * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it.
     * <p>
     * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using
     * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this
     * destructive method.
     *
     * @throws UnsupportedOperationException
     *             if engine is managed
     */
    public abstract void dispose();

    /**
     * Provides access to the selected query backend component of the VIATRA Query engine.
     * @noreference for internal use only
     * @throws ViatraQueryRuntimeException
     */
    public abstract IQueryBackend getQueryBackend(IQueryBackendFactory iQueryBackendFactory);

    /**
     * Access an existing pattern matcher based on a {@link IQuerySpecification}, and optional hints override.
     * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification
     * @param optionalOverrideHints a {@link QueryEvaluationHint} that may override the pattern hints (can be null)
     * @return a pattern matcher corresponding to the specification, <code>null</code> if a matcher does not exist yet.
     * @since 1.4
     */
    public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher(IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalOverrideHints);

    /**
     * Returns the immutable {@link ViatraQueryEngineOptions} of the engine.
     *
     * @return the engine options
     * @since 1.4
     */
    public abstract ViatraQueryEngineOptions getEngineOptions();

    /**
     * Return the underlying result provider for the given matcher.
     *
     * @beta This method may change in future versions
     * @since 1.4
     * @noreference This method is considered internal API
     */
    public abstract IQueryResultProvider getResultProviderOfMatcher(ViatraQueryMatcher<? extends IPatternMatch> matcher);

    /**
     * The given callable will be executed, and all update propagation in stateful query backends
     * will be delayed until the execution is done. Within the callback, these backends will provide stale results.
     *
     * <p> It is optional for a {@link IQueryBackend} to support the delaying of update propagation; stateless backends will display up-to-date results.
     * In this case, the given callable shall be executed, and the update propagation shall happen just like in non-delayed execution.
     *
     * <p> Example: in the Rete network, no messages will be propagated until the given callable is executed.
     * After the execution of the callable, all accumulated messages will be delivered.
     *
     * <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.
     *
     * @param callable the callable to be executed
     * @return the result of the callable
     * @since 1.6
     */
    public abstract <V> V delayUpdatePropagation(Callable<V> callable) throws InvocationTargetException;

    /**
     * Returns true if the update propagation in this engine is currently delayed, false otherwise.
     *
     * @see {@link #delayUpdatePropagation(Callable)}
     * @since 1.6
     */
    public abstract boolean isUpdatePropagationDelayed();

    /**
     * Returns true if the {@link #dispose()} method was called on this engine previously.
     * @since 2.0
     */
    public abstract boolean isDisposed();
}