diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-08-19 02:31:57 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-08-19 02:31:57 +0200 |
commit | 9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b (patch) | |
tree | fad77963c1dc9028b0a767ac3ad69a174c8eb8c6 /subprojects/viatra-runtime/src/main | |
parent | feat: predicate semantics (diff) | |
download | refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.gz refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.zst refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.zip |
chore: import VIATRA source
Make our modifications more maintainable by editing the source code directly
instead of using reflection.
Diffstat (limited to 'subprojects/viatra-runtime/src/main')
93 files changed, 10042 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java new file mode 100644 index 00000000..d5e0d51f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java | |||
@@ -0,0 +1,24 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann, 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; | ||
11 | |||
12 | /** | ||
13 | * Interface for storing string constants related to VIATRA Query's extension points. | ||
14 | * | ||
15 | * @author Istvan Rath | ||
16 | * | ||
17 | */ | ||
18 | public interface IExtensions { | ||
19 | |||
20 | public static final String QUERY_SPECIFICATION_EXTENSION_POINT_ID = ViatraQueryRuntimePlugin.PLUGIN_ID + ".queryspecification"; | ||
21 | |||
22 | public static final String INJECTOREXTENSIONID = ViatraQueryRuntimePlugin.PLUGIN_ID + ".injectorprovider"; | ||
23 | |||
24 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/ViatraQueryRuntimePlugin.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/ViatraQueryRuntimePlugin.java new file mode 100644 index 00000000..5fbcdad0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/ViatraQueryRuntimePlugin.java | |||
@@ -0,0 +1,32 @@ | |||
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 | package tools.refinery.viatra.runtime; | ||
10 | |||
11 | import org.eclipse.core.runtime.Plugin; | ||
12 | import tools.refinery.viatra.runtime.internal.ExtensionBasedSurrogateQueryLoader; | ||
13 | import tools.refinery.viatra.runtime.internal.ExtensionBasedSystemDefaultBackendLoader; | ||
14 | import tools.refinery.viatra.runtime.registry.ExtensionBasedQuerySpecificationLoader; | ||
15 | import org.osgi.framework.BundleContext; | ||
16 | |||
17 | /** | ||
18 | * The activator class controls the plug-in life cycle | ||
19 | */ | ||
20 | public class ViatraQueryRuntimePlugin extends Plugin { | ||
21 | |||
22 | public static final String PLUGIN_ID = "tools.refinery.viatra.runtime"; | ||
23 | |||
24 | @Override | ||
25 | public void start(BundleContext context) throws Exception { | ||
26 | super.start(context); | ||
27 | ExtensionBasedSurrogateQueryLoader.instance().loadKnownSurrogateQueriesIntoRegistry(); | ||
28 | ExtensionBasedQuerySpecificationLoader.getInstance().loadRegisteredQuerySpecificationsIntoRegistry(); | ||
29 | ExtensionBasedSystemDefaultBackendLoader.instance().loadKnownBackends(); | ||
30 | } | ||
31 | |||
32 | } | ||
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..21e7dfa3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java | |||
@@ -0,0 +1,368 @@ | |||
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 java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.concurrent.Callable; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
15 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
16 | import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl; | ||
17 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
18 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
19 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
20 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
23 | |||
24 | /** | ||
25 | * Advanced interface to a VIATRA incremental evaluation engine. | ||
26 | * | ||
27 | * <p> | ||
28 | * You can create a new, private, unmanaged {@link AdvancedViatraQueryEngine} instance using | ||
29 | * {@link #createUnmanagedEngine(QueryScope)}. Additionally, you can access the advanced interface on any | ||
30 | * {@link ViatraQueryEngine} by {@link AdvancedViatraQueryEngine#from(ViatraQueryEngine)}. | ||
31 | * | ||
32 | * <p> | ||
33 | * While the default interface {@link ViatraQueryEngine}, is suitable for most users, this advanced interface provides more | ||
34 | * control over the engine. The most important added functionality is the following: | ||
35 | * <ul> | ||
36 | * <li>You can have tighter control over the lifecycle of the engine, if you create a private, unmanaged engine | ||
37 | * instance. For instance, a (non-managed) engine can be disposed in order to detach from the EMF model and stop | ||
38 | * listening on update notifications. The indexes built previously in the engine can then be garbage collected, even if | ||
39 | * the model itself is retained. Total lifecycle control is only available for private, unmanaged engines (created using | ||
40 | * {@link #createUnmanagedEngine(QueryScope)}); a managed engine (obtained via {@link ViatraQueryEngine#on(QueryScope)}) is | ||
41 | * shared among clients and can not be disposed or wiped. | ||
42 | * <li>You can add and remove listeners to receive notification when the model or the match sets change. | ||
43 | * <li>You can add and remove listeners to receive notification on engine lifecycle events, such as creation of new | ||
44 | * matchers. For instance, if you explicitly share a private, unmanaged engine between multiple sites, you should | ||
45 | * register a callback using {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client | ||
46 | * has called the destructive methods {@link #dispose()} or {@link #wipe()}. | ||
47 | * </ul> | ||
48 | * | ||
49 | * @author Bergmann Gabor | ||
50 | * @noextend This class is not intended to be subclassed by clients. | ||
51 | */ | ||
52 | public abstract class AdvancedViatraQueryEngine extends ViatraQueryEngine { | ||
53 | |||
54 | /** | ||
55 | * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
56 | * | ||
57 | * <p> Repeated invocations will return different instances, so other clients are unable to independently access | ||
58 | * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements | ||
59 | * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed | ||
60 | * engine instance. | ||
61 | * | ||
62 | * <p> | ||
63 | * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface | ||
64 | * {@link AdvancedViatraQueryEngine}. | ||
65 | * | ||
66 | * <p> | ||
67 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
68 | * | ||
69 | * @param scope | ||
70 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
71 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
72 | * @return the advanced interface to a newly created unmanaged engine | ||
73 | * @since 0.9 | ||
74 | */ | ||
75 | public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope) { | ||
76 | return new ViatraQueryEngineImpl(null, scope); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Creates a new unmanaged VIATRA Query engine to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
81 | * | ||
82 | * <p> Repeated invocations will return different instances, so other clients are unable to independently access | ||
83 | * and influence the returned engine. Note that unmanaged engines do not benefit from some performance improvements | ||
84 | * that stem from sharing incrementally maintained indices and caches between multiple clients using the same managed | ||
85 | * engine instance. | ||
86 | * | ||
87 | * <p> | ||
88 | * Client is responsible for the lifecycle of the returned engine, hence the usage of the advanced interface | ||
89 | * {@link AdvancedViatraQueryEngine}. | ||
90 | * | ||
91 | * <p> | ||
92 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
93 | * | ||
94 | * @param scope | ||
95 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
96 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
97 | * @return the advanced interface to a newly created unmanaged engine | ||
98 | * @since 1.4 | ||
99 | */ | ||
100 | public static AdvancedViatraQueryEngine createUnmanagedEngine(QueryScope scope, ViatraQueryEngineOptions options) { | ||
101 | return new ViatraQueryEngineImpl(null, scope, options); | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * Provides access to a given existing engine through the advanced interface. | ||
106 | * | ||
107 | * <p> | ||
108 | * Caveat: if the referenced engine is managed (i.e. created via {@link ViatraQueryEngine#on(QueryScope)}), the advanced | ||
109 | * methods {@link #dispose()} and {@link #wipe()} will not be allowed. | ||
110 | * | ||
111 | * @param engine | ||
112 | * the engine to access using the advanced interface | ||
113 | * @return a reference to the same engine conforming to the advanced interface | ||
114 | */ | ||
115 | public static AdvancedViatraQueryEngine from(ViatraQueryEngine engine) { | ||
116 | return (AdvancedViatraQueryEngine) engine; | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * Add an engine lifecycle listener to this engine instance. | ||
121 | * | ||
122 | * @param listener | ||
123 | * the {@link ViatraQueryEngineLifecycleListener} that should listen to lifecycle events from this engine | ||
124 | */ | ||
125 | public abstract void addLifecycleListener(ViatraQueryEngineLifecycleListener listener); | ||
126 | |||
127 | /** | ||
128 | * Remove an existing lifecycle listener from this engine instance. | ||
129 | * | ||
130 | * @param listener | ||
131 | * the {@link ViatraQueryEngineLifecycleListener} that should not listen to lifecycle events from this | ||
132 | * engine anymore | ||
133 | */ | ||
134 | public abstract void removeLifecycleListener(ViatraQueryEngineLifecycleListener listener); | ||
135 | |||
136 | /** | ||
137 | * Add an model update event listener to this engine instance (that fires its callbacks according to its | ||
138 | * notification level). | ||
139 | * | ||
140 | * @param listener | ||
141 | * the {@link ViatraQueryModelUpdateListener} that should listen to model update events from this engine. | ||
142 | */ | ||
143 | public abstract void addModelUpdateListener(ViatraQueryModelUpdateListener listener); | ||
144 | |||
145 | /** | ||
146 | * Remove an existing model update event listener to this engine instance. | ||
147 | * | ||
148 | * @param listener | ||
149 | * the {@link ViatraQueryModelUpdateListener} that should not listen to model update events from this engine | ||
150 | * anymore | ||
151 | */ | ||
152 | public abstract void removeModelUpdateListener(ViatraQueryModelUpdateListener listener); | ||
153 | |||
154 | /** | ||
155 | * Registers low-level callbacks for match appearance and disappearance on this pattern matcher. | ||
156 | * | ||
157 | * <p> | ||
158 | * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a | ||
159 | * consistent state yet. Importantly, no model modification permitted during the callback. Most users should use the | ||
160 | * databinding support ({@link org.eclipse.viatra.addon.databinding.runtime.api.ViatraObservables ViatraObservables}) or the event-driven API | ||
161 | * ({@link org.eclipse.viatra.transformation.evm.api.EventDrivenVM EventDrivenVM}) instead. | ||
162 | * | ||
163 | * <p> | ||
164 | * Performance note: expected to be much more efficient than polling at {@link #addCallbackAfterUpdates(Runnable)}, | ||
165 | * but prone to "signal hazards", e.g. spurious match appearances that will disappear immediately afterwards. | ||
166 | * | ||
167 | * <p> | ||
168 | * The callback can be unregistered via {@link #removeCallbackOnMatchUpdate(IMatchUpdateListener)}. | ||
169 | * | ||
170 | * @param fireNow | ||
171 | * if true, appearCallback will be immediately invoked on all current matches as a one-time effect. See | ||
172 | * also {@link ViatraQueryMatcher#forEachMatch(IMatchProcessor)}. | ||
173 | * @param listener | ||
174 | * the listener that will be notified of each new match that appears or disappears, starting from now. | ||
175 | * @param matcher | ||
176 | * the {@link ViatraQueryMatcher} for which this listener should be active | ||
177 | */ | ||
178 | public abstract <Match extends IPatternMatch> void addMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
179 | IMatchUpdateListener<? super Match> listener, boolean fireNow); | ||
180 | |||
181 | /** | ||
182 | * Remove an existing match update event listener to this engine instance. | ||
183 | * | ||
184 | * @param matcher | ||
185 | * the {@link ViatraQueryMatcher} for which this listener should not be active anymore | ||
186 | * @param listener | ||
187 | * the {@link IMatchUpdateListener} that should not receive the callbacks anymore | ||
188 | */ | ||
189 | public abstract <Match extends IPatternMatch> void removeMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
190 | IMatchUpdateListener<? super Match> listener); | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Access a pattern matcher based on a {@link IQuerySpecification}, overriding some of the default query evaluation hints. | ||
195 | * Multiple calls may return the same matcher depending on the actual evaluation hints. | ||
196 | * | ||
197 | * <p> It is guaranteed that this method will always return a matcher instance which is functionally compatible | ||
198 | * with the requested functionality (see {@link IMatcherCapability}). | ||
199 | * Otherwise, the query evaluator is free to ignore any hints. | ||
200 | * | ||
201 | * <p> For stateful query backends (Rete), hints may be effective only the first time a matcher is created. | ||
202 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query | ||
203 | * @return a pattern matcher corresponding to the specification | ||
204 | * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with the query | ||
205 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
206 | * @since 0.9 | ||
207 | */ | ||
208 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
209 | IQuerySpecification<Matcher> querySpecification, | ||
210 | QueryEvaluationHint optionalEvaluationHints); | ||
211 | |||
212 | /** | ||
213 | * Initializes matchers for a group of patterns as one step (optionally overriding some of the default query evaluation hints). | ||
214 | * If some of the pattern matchers are already | ||
215 | * constructed in the engine, no task is performed for them. | ||
216 | * | ||
217 | * <p> | ||
218 | * This preparation step has the advantage that it prepares pattern matchers for an arbitrary number of patterns in a | ||
219 | * single-pass traversal of the model. | ||
220 | * This is typically more efficient than traversing the model each time an individual pattern matcher is initialized on demand. | ||
221 | * The performance benefit only manifests itself if the engine is not in wildcard mode. | ||
222 | * | ||
223 | * @param queryGroup a {@link IQueryGroup} identifying a set of VIATRA queries | ||
224 | * @param optionalEvaluationHints additional / overriding options on query evaluation; passing null means default options associated with each query | ||
225 | * @throws ViatraQueryRuntimeException | ||
226 | * if there was an error in preparing the engine | ||
227 | * @since 0.9 | ||
228 | */ | ||
229 | public abstract void prepareGroup(IQueryGroup queryGroup, QueryEvaluationHint optionalEvaluationHints); | ||
230 | |||
231 | /** | ||
232 | * Indicates whether the engine is managed, i.e. the default engine assigned to the given scope root by | ||
233 | * {@link ViatraQueryEngine#on(QueryScope)}. | ||
234 | * | ||
235 | * <p> | ||
236 | * If the engine is managed, there may be other clients using it, as all calls to | ||
237 | * {@link ViatraQueryEngine#on(QueryScope)} return the same managed engine instance for a given scope root. Therefore the | ||
238 | * destructive methods {@link #wipe()} and {@link #dispose()} are not allowed. | ||
239 | * | ||
240 | * <p> | ||
241 | * On the other hand, if the engine is unmanaged (i.e. a private instance created using | ||
242 | * {@link #createUnmanagedEngine(QueryScope)}), then {@link #wipe()} and {@link #dispose()} can be called. If you | ||
243 | * explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
244 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called these | ||
245 | * destructive methods. | ||
246 | * | ||
247 | * @return true if the engine is managed, and therefore potentially shared with other clients querying the same EMF | ||
248 | * model | ||
249 | */ | ||
250 | public abstract boolean isManaged(); | ||
251 | |||
252 | /** | ||
253 | * Indicates whether the engine is in a tainted, inconsistent state due to some internal errors. If true, results | ||
254 | * are no longer reliable; engine should be disposed. | ||
255 | * | ||
256 | * <p> | ||
257 | * The engine is in a tainted state if any of its internal processes report back a fatal error. The | ||
258 | * {@link ViatraQueryEngineLifecycleListener} interface provides a callback method for entering the tainted state. | ||
259 | * | ||
260 | * @return the tainted state | ||
261 | */ | ||
262 | public abstract boolean isTainted(); | ||
263 | |||
264 | /** | ||
265 | * Discards any pattern matcher caches and forgets known patterns. The base index built directly on the underlying | ||
266 | * EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. Use this method if you | ||
267 | * have e.g. new versions of the same patterns, to be matched on the same model. | ||
268 | * | ||
269 | * <p> | ||
270 | * Matcher objects will continue to return stale results. If no references are retained to the matchers, they can | ||
271 | * eventually be GC'ed. | ||
272 | * <p> | ||
273 | * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it. | ||
274 | * <p> | ||
275 | * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
276 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this | ||
277 | * destructive method. | ||
278 | * | ||
279 | * @throws UnsupportedOperationException | ||
280 | * if engine is managed | ||
281 | */ | ||
282 | public abstract void wipe(); | ||
283 | |||
284 | /** | ||
285 | * Completely disconnects and dismantles the engine. Cannot be reversed. | ||
286 | * <p> | ||
287 | * Matcher objects will continue to return stale results. If no references are retained to the matchers or the | ||
288 | * engine, they can eventually be GC'ed, and they won't block the EMF model from being GC'ed anymore. | ||
289 | * <p> | ||
290 | * The base indexer (see {@link #getBaseIndex()}) built on the model will be disposed alongside the engine, unless | ||
291 | * the user has manually added listeners on the base index that were not removed yet. | ||
292 | * <p> | ||
293 | * Disallowed if the engine is managed (see {@link #isManaged()}), as there may be other clients using it. | ||
294 | * <p> | ||
295 | * If you explicitly share a private, unmanaged engine between multiple sites, register a callback using | ||
296 | * {@link #addLifecycleListener(ViatraQueryEngineLifecycleListener)} to learn when another client has called this | ||
297 | * destructive method. | ||
298 | * | ||
299 | * @throws UnsupportedOperationException | ||
300 | * if engine is managed | ||
301 | */ | ||
302 | public abstract void dispose(); | ||
303 | |||
304 | /** | ||
305 | * Provides access to the selected query backend component of the VIATRA Query engine. | ||
306 | * @noreference for internal use only | ||
307 | * @throws ViatraQueryRuntimeException | ||
308 | */ | ||
309 | public abstract IQueryBackend getQueryBackend(IQueryBackendFactory iQueryBackendFactory); | ||
310 | |||
311 | /** | ||
312 | * Access an existing pattern matcher based on a {@link IQuerySpecification}, and optional hints override. | ||
313 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
314 | * @param optionalOverrideHints a {@link QueryEvaluationHint} that may override the pattern hints (can be null) | ||
315 | * @return a pattern matcher corresponding to the specification, <code>null</code> if a matcher does not exist yet. | ||
316 | * @since 1.4 | ||
317 | */ | ||
318 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher(IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalOverrideHints); | ||
319 | |||
320 | /** | ||
321 | * Returns the immutable {@link ViatraQueryEngineOptions} of the engine. | ||
322 | * | ||
323 | * @return the engine options | ||
324 | * @since 1.4 | ||
325 | */ | ||
326 | public abstract ViatraQueryEngineOptions getEngineOptions(); | ||
327 | |||
328 | /** | ||
329 | * Return the underlying result provider for the given matcher. | ||
330 | * | ||
331 | * @beta This method may change in future versions | ||
332 | * @since 1.4 | ||
333 | * @noreference This method is considered internal API | ||
334 | */ | ||
335 | public abstract IQueryResultProvider getResultProviderOfMatcher(ViatraQueryMatcher<? extends IPatternMatch> matcher); | ||
336 | |||
337 | /** | ||
338 | * The given callable will be executed, and all update propagation in stateful query backends | ||
339 | * will be delayed until the execution is done. Within the callback, these backends will provide stale results. | ||
340 | * | ||
341 | * <p> It is optional for a {@link IQueryBackend} to support the delaying of update propagation; stateless backends will display up-to-date results. | ||
342 | * In this case, the given callable shall be executed, and the update propagation shall happen just like in non-delayed execution. | ||
343 | * | ||
344 | * <p> Example: in the Rete network, no messages will be propagated until the given callable is executed. | ||
345 | * After the execution of the callable, all accumulated messages will be delivered. | ||
346 | * | ||
347 | * <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. | ||
348 | * | ||
349 | * @param callable the callable to be executed | ||
350 | * @return the result of the callable | ||
351 | * @since 1.6 | ||
352 | */ | ||
353 | public abstract <V> V delayUpdatePropagation(Callable<V> callable) throws InvocationTargetException; | ||
354 | |||
355 | /** | ||
356 | * Returns true if the update propagation in this engine is currently delayed, false otherwise. | ||
357 | * | ||
358 | * @see {@link #delayUpdatePropagation(Callable)} | ||
359 | * @since 1.6 | ||
360 | */ | ||
361 | public abstract boolean isUpdatePropagationDelayed(); | ||
362 | |||
363 | /** | ||
364 | * Returns true if the {@link #dispose()} method was called on this engine previously. | ||
365 | * @since 2.0 | ||
366 | */ | ||
367 | public abstract boolean isDisposed(); | ||
368 | } | ||
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/IModelConnectorTypeEnum.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IModelConnectorTypeEnum.java new file mode 100644 index 00000000..e672a6e2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IModelConnectorTypeEnum.java | |||
@@ -0,0 +1,18 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Andras Okros, 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 | * This enum represents the possible notifier types which a model input should provide for the ui. | ||
13 | */ | ||
14 | public enum IModelConnectorTypeEnum { | ||
15 | |||
16 | RESOURCESET, RESOURCE; | ||
17 | |||
18 | } | ||
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..9b84b031 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQuerySpecification.java | |||
@@ -0,0 +1,87 @@ | |||
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.emf.EMFScope; | ||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueryHeader; | ||
17 | |||
18 | /** | ||
19 | * API interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher | ||
20 | * of the pattern with various parameters. | ||
21 | * | ||
22 | * <p> As of 0.9.0, some internal details (mostly relevant for query evaluator backends) have been moved to {@link #getInternalQueryRepresentation()}. | ||
23 | * | ||
24 | * @author Bergmann Gábor | ||
25 | * | ||
26 | */ | ||
27 | public interface IQuerySpecification<Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> extends PQueryHeader { | ||
28 | |||
29 | /** | ||
30 | * Initializes the pattern matcher within an existing {@link ViatraQueryEngine}. If the pattern matcher is already | ||
31 | * constructed in the engine, only a lightweight reference is created. | ||
32 | * <p> | ||
33 | * The match set will be incrementally refreshed upon updates. | ||
34 | * | ||
35 | * @param engine | ||
36 | * the existing VIATRA Query engine in which this matcher will be created. | ||
37 | * @throws ViatraQueryRuntimeException | ||
38 | * if an error occurs during pattern matcher creation | ||
39 | */ | ||
40 | public Matcher getMatcher(ViatraQueryEngine engine); | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Returns an empty, mutable Match compatible with matchers of this query. | ||
45 | * Fields of the mutable match can be filled to create a partial match, usable as matcher input. | ||
46 | * This can be used to call the matcher with a partial match | ||
47 | * even if the specific class of the matcher or the match is unknown. | ||
48 | * | ||
49 | * @return the empty match | ||
50 | */ | ||
51 | public abstract IPatternMatch newEmptyMatch(); | ||
52 | |||
53 | /** | ||
54 | * Returns a new (partial) Match object compatible with matchers of this query. | ||
55 | * This can be used e.g. to call the matcher with a partial | ||
56 | * match. | ||
57 | * | ||
58 | * <p>The returned match will be immutable. Use {@link #newEmptyMatch()} to obtain a mutable match object. | ||
59 | * | ||
60 | * @param parameters | ||
61 | * the fixed value of pattern parameters, or null if not bound. | ||
62 | * @return the (partial) match object. | ||
63 | */ | ||
64 | public abstract IPatternMatch newMatch(Object... parameters); | ||
65 | |||
66 | /** | ||
67 | * The query is formulated over this kind of modeling platform. | ||
68 | * E.g. for queries over EMF models, the {@link EMFScope} class is returned. | ||
69 | */ | ||
70 | public Class<? extends QueryScope> getPreferredScopeClass(); | ||
71 | |||
72 | /** | ||
73 | * Returns the definition of the query in a format intended for consumption by the query evaluator. | ||
74 | * @return the internal representation of the query. | ||
75 | */ | ||
76 | public PQuery getInternalQueryRepresentation(); | ||
77 | |||
78 | /** | ||
79 | * Creates a new uninitialized matcher, which is not functional until an engine initializes it. Clients | ||
80 | * should not call this method, it is used by the {@link ViatraQueryEngine} instance to instantiate matchers. | ||
81 | * @throws ViatraQueryRuntimeException | ||
82 | * @noreference This method is not intended to be referenced by clients. | ||
83 | * @since 1.4 | ||
84 | */ | ||
85 | public Matcher instantiate(); | ||
86 | |||
87 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IRunOnceQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IRunOnceQueryEngine.java new file mode 100644 index 00000000..b625980b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IRunOnceQueryEngine.java | |||
@@ -0,0 +1,68 @@ | |||
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 | import java.util.Collection; | ||
12 | |||
13 | import org.eclipse.emf.common.notify.Notifier; | ||
14 | import tools.refinery.viatra.runtime.base.api.BaseIndexOptions; | ||
15 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
16 | |||
17 | /** | ||
18 | * A run-once query engine is used to get matches for queries without incremental support. | ||
19 | * Users can create a query engine with a given {@link Notifier} as scope and use a query specification | ||
20 | * to retrieve the current match set with this scope (see {@link #getAllMatches}). | ||
21 | * | ||
22 | * @author Abel Hegedus | ||
23 | * | ||
24 | */ | ||
25 | public interface IRunOnceQueryEngine { | ||
26 | |||
27 | /** | ||
28 | * Returns the set of all matches for the given query in the scope of the engine. | ||
29 | * | ||
30 | * @param querySpecification the query that is evaluated | ||
31 | * @return matches represented as a Match object. | ||
32 | */ | ||
33 | <Match extends IPatternMatch> Collection<Match> getAllMatches( | ||
34 | final IQuerySpecification<? extends ViatraQueryMatcher<Match>> querySpecification); | ||
35 | |||
36 | /** | ||
37 | * @return the scope of pattern matching, i.e. the root of the EMF model tree that this engine is attached to. | ||
38 | */ | ||
39 | Notifier getScope(); | ||
40 | |||
41 | /** | ||
42 | * The base index options specifies how the base index is built, including wildcard mode (defaults to false) and | ||
43 | * dynamic EMF mode (defaults to false). See {@link NavigationHelper} for the explanation of wildcard mode and | ||
44 | * dynamic EMF mode. | ||
45 | * | ||
46 | * <p/> The returned options can be modified in order to affect subsequent calls of {@link #getAllMatches}. | ||
47 | * | ||
48 | * @return the base index options used by the engine. | ||
49 | */ | ||
50 | BaseIndexOptions getBaseIndexOptions(); | ||
51 | |||
52 | /** | ||
53 | * When set to true, the run-once query engine will not dispose it's engine and will resample the values of derived | ||
54 | * features before returning matches if the model changed since the last call. | ||
55 | * | ||
56 | * If the values of derived features may change without any model modification, call {@link #resampleOnNextCall()} | ||
57 | * before subsequent calls of {@link #getAllMatches}. | ||
58 | * | ||
59 | * @param automaticResampling | ||
60 | */ | ||
61 | void setAutomaticResampling(boolean automaticResampling); | ||
62 | |||
63 | /** | ||
64 | * If automatic resampling is enabled and the value of derived features may change without model modifications, | ||
65 | * calling this method will make sure that re-sampling will occur before returning match results. | ||
66 | */ | ||
67 | void resampleOnNextCall(); | ||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/LazyLoadingQueryGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/LazyLoadingQueryGroup.java new file mode 100644 index 00000000..6ae44b7c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/LazyLoadingQueryGroup.java | |||
@@ -0,0 +1,64 @@ | |||
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.api; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Objects; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.Supplier; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.api.impl.BaseQueryGroup; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
20 | |||
21 | /** | ||
22 | * Initializes a query group from a set of query providers. The query providers are not executed until the queries | ||
23 | * themselves are asked in the {@link #getSpecifications()} method. | ||
24 | * | ||
25 | * @author Zoltan Ujhelyi | ||
26 | * @since 1.3 | ||
27 | * | ||
28 | */ | ||
29 | public class LazyLoadingQueryGroup extends BaseQueryGroup { | ||
30 | |||
31 | private final Set<? extends Supplier<IQuerySpecification<?>>> providers; | ||
32 | private Set<IQuerySpecification<?>> specifications = null; | ||
33 | |||
34 | /** | ||
35 | * @param providers a non-null set to initialize the group | ||
36 | */ | ||
37 | public LazyLoadingQueryGroup(Set<? extends Supplier<IQuerySpecification<?>>> providers) { | ||
38 | Preconditions.checkArgument(providers != null, "The set of providers must not be null"); | ||
39 | this.providers = providers; | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * @param providers a non-null set to initialize the group | ||
44 | */ | ||
45 | public static IQueryGroup of(Set<? extends Supplier<IQuerySpecification<?>>> querySpecifications) { | ||
46 | return new LazyLoadingQueryGroup(querySpecifications); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Set<IQuerySpecification<?>> getSpecifications() { | ||
51 | if (specifications == null) { | ||
52 | try { | ||
53 | specifications = providers.stream().filter(Objects::nonNull).map(Supplier::get).filter(Objects::nonNull).collect(Collectors.toSet()); | ||
54 | } catch (Exception e) { | ||
55 | // TODO maybe store in issue list and provide better error reporting in general | ||
56 | String errorMessage = "Exception occurred while accessing query specification from provider: " + e.getMessage(); | ||
57 | ViatraQueryLoggingUtil.getLogger(getClass()).error(errorMessage); | ||
58 | return Collections.emptySet(); | ||
59 | } | ||
60 | } | ||
61 | return specifications; | ||
62 | } | ||
63 | |||
64 | } | ||
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/PackageBasedQueryGroup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/PackageBasedQueryGroup.java new file mode 100644 index 00000000..252bb7fd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/PackageBasedQueryGroup.java | |||
@@ -0,0 +1,133 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Abel Hegedus, 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.Collections; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.api.impl.BaseQueryGroup; | ||
16 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
17 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
18 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener; | ||
19 | import tools.refinery.viatra.runtime.registry.IRegistryView; | ||
20 | import tools.refinery.viatra.runtime.registry.IRegistryViewFilter; | ||
21 | import tools.refinery.viatra.runtime.registry.QuerySpecificationRegistry; | ||
22 | |||
23 | /** | ||
24 | * Package based {@link BaseQueryGroup} implementation. It handles patterns as a group within the same package. | ||
25 | * | ||
26 | * @author Abel Hegedus, Mark Czotter | ||
27 | * | ||
28 | */ | ||
29 | public class PackageBasedQueryGroup extends BaseQueryGroup { | ||
30 | |||
31 | private final Set<IQuerySpecification<?>> querySpecifications = new HashSet<>(); | ||
32 | private final String packageName; | ||
33 | private final boolean includeSubPackages; | ||
34 | private IRegistryView view; | ||
35 | |||
36 | /** | ||
37 | * Creates a query group with specifications of a given package from the {@link QuerySpecificationRegistry}. Only | ||
38 | * query specifications with the exact package fully qualified name are included. | ||
39 | * | ||
40 | * @param packageName | ||
41 | * that contains the specifications | ||
42 | */ | ||
43 | public PackageBasedQueryGroup(String packageName) { | ||
44 | this(packageName, false); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Creates a query group with specifications of a given package from the {@link QuerySpecificationRegistry}. | ||
49 | * | ||
50 | * @param packageName | ||
51 | * that contain the specifications | ||
52 | * @param includeSubPackages | ||
53 | * if true all query specifications with package names starting with the given package are included | ||
54 | */ | ||
55 | public PackageBasedQueryGroup(String packageName, boolean includeSubPackages) { | ||
56 | super(); | ||
57 | this.packageName = packageName; | ||
58 | this.includeSubPackages = includeSubPackages; | ||
59 | IQuerySpecificationRegistry registry = QuerySpecificationRegistry.getInstance(); | ||
60 | view = registry.createView(new PackageNameBasedViewFilter()); | ||
61 | for (IQuerySpecificationRegistryEntry entry : view.getEntries()) { | ||
62 | this.querySpecifications.add(entry.get()); | ||
63 | } | ||
64 | SpecificationSetUpdater listener = new SpecificationSetUpdater(); | ||
65 | view.addViewListener(listener); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Set<IQuerySpecification<?>> getSpecifications() { | ||
70 | return Collections.unmodifiableSet(new HashSet<>(querySpecifications)); | ||
71 | } | ||
72 | |||
73 | public String getPackageName() { | ||
74 | return packageName; | ||
75 | } | ||
76 | |||
77 | public boolean isIncludeSubPackages() { | ||
78 | return includeSubPackages; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Refreshes the pattern group from the query specification registry based on the parameters used during the | ||
83 | * initialization. | ||
84 | */ | ||
85 | public void refresh() { | ||
86 | // do nothing, view is automatically refreshed | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * Listener to update the specification set | ||
91 | * | ||
92 | * @author Abel Hegedus | ||
93 | * | ||
94 | */ | ||
95 | private final class SpecificationSetUpdater implements IQuerySpecificationRegistryChangeListener { | ||
96 | @Override | ||
97 | public void entryAdded(IQuerySpecificationRegistryEntry entry) { | ||
98 | querySpecifications.add(entry.get()); | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public void entryRemoved(IQuerySpecificationRegistryEntry entry) { | ||
103 | querySpecifications.remove(entry.get()); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * Registry view filter that checks FQNs against the given package name. | ||
109 | * | ||
110 | * @author Abel Hegedus | ||
111 | * | ||
112 | */ | ||
113 | private final class PackageNameBasedViewFilter implements IRegistryViewFilter { | ||
114 | @Override | ||
115 | public boolean isEntryRelevant(IQuerySpecificationRegistryEntry entry) { | ||
116 | String fqn = entry.getFullyQualifiedName(); | ||
117 | if (packageName.length() + 1 < fqn.length()) { | ||
118 | if (includeSubPackages) { | ||
119 | if (fqn.startsWith(packageName + '.')) { | ||
120 | return true; | ||
121 | } | ||
122 | } else { | ||
123 | String name = fqn.substring(fqn.lastIndexOf('.') + 1, fqn.length()); | ||
124 | if (fqn.equals(packageName + '.' + name)) { | ||
125 | return true; | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | return false; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | } | ||
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..fd8ff848 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java | |||
@@ -0,0 +1,152 @@ | |||
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 org.eclipse.emf.common.notify.Notifier; | ||
13 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
15 | import tools.refinery.viatra.runtime.base.api.BaseIndexOptions; | ||
16 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
17 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
18 | |||
19 | import java.util.Set; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | /** | ||
23 | * A Viatra Query (incremental) evaluation engine, attached to a model such as an EMF resource. The engine hosts pattern matchers, and | ||
24 | * will listen on model update notifications stemming from the given model in order to maintain live results. | ||
25 | * | ||
26 | * <p> | ||
27 | * By default, ViatraQueryEngines do not need to be separately disposed; they will be garbage collected along with the model. | ||
28 | * Advanced users: see {@link AdvancedViatraQueryEngine} if you want fine control over the lifecycle of an engine. | ||
29 | * | ||
30 | * <p> | ||
31 | * Pattern matchers within this engine may be instantiated in the following ways: | ||
32 | * <ul> | ||
33 | * <li>Recommended: instantiate the specific matcher class generated for the pattern by e.g. MyPatternMatcher.on(engine). | ||
34 | * <li>Use {@link #getMatcher(IQuerySpecification)} if the pattern-specific generated matcher API is not available. | ||
35 | * <li>Advanced: use the query specification associated with the generated matcher class to achieve the same. | ||
36 | * </ul> | ||
37 | * Additionally, a group of patterns (see {@link IQueryGroup}) can be initialized together before usage; this may improve | ||
38 | * the performance of pattern matcher construction by trying to gather all necessary information from the model in one go. | ||
39 | * Note that no such improvement is to be expected if the engine is specifically constructed in wildcard mode, | ||
40 | * an option available in some scope implementations | ||
41 | * (see {@link EMFScope#EMFScope(Notifier, BaseIndexOptions)} and {@link BaseIndexOptions#withWildcardMode(boolean)}). | ||
42 | * | ||
43 | * | ||
44 | * @author Bergmann Gábor | ||
45 | * @noextend This class is not intended to be subclassed by clients. | ||
46 | */ | ||
47 | public abstract class ViatraQueryEngine { | ||
48 | |||
49 | |||
50 | /** | ||
51 | * Obtain a (managed) {@link ViatraQueryEngine} to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
52 | * | ||
53 | * <p> For a given matcher scope, the same engine will be returned to any client. | ||
54 | * This facilitates the reuse of internal caches of the engine, greatly improving performance. | ||
55 | * | ||
56 | * <p> The lifecycle of this engine is centrally managed, and will not be disposed as long as the model is retained in memory. | ||
57 | * The engine will be garbage collected along with the model. | ||
58 | * | ||
59 | * <p> | ||
60 | * Advanced users: see {@link AdvancedViatraQueryEngine#createUnmanagedEngine(QueryScope)} to obtain a private, | ||
61 | * unmanaged engine that is not shared with other clients and allows tight control over its lifecycle. | ||
62 | * | ||
63 | * @param scope | ||
64 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
65 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
66 | * @return a (managed) {@link ViatraQueryEngine} instance | ||
67 | */ | ||
68 | public static ViatraQueryEngine on(QueryScope scope) { | ||
69 | return ViatraQueryEngineManager.getInstance().getQueryEngine(scope); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Obtain a (managed) {@link ViatraQueryEngine} to evaluate queries over a given scope specified by an {@link QueryScope}. | ||
74 | * | ||
75 | * <p> For a given matcher scope, the same engine will be returned to any client. | ||
76 | * This facilitates the reuse of internal caches of the engine, greatly improving performance. | ||
77 | * | ||
78 | * <p> The lifecycle of this engine is centrally managed, and will not be disposed as long as the model is retained in memory. | ||
79 | * The engine will be garbage collected along with the model. | ||
80 | * | ||
81 | * <p> | ||
82 | * Advanced users: see {@link AdvancedViatraQueryEngine#createUnmanagedEngine(QueryScope)} to obtain a private, | ||
83 | * unmanaged engine that is not shared with other clients and allows tight control over its lifecycle. | ||
84 | * | ||
85 | * @param scope | ||
86 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
87 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
88 | * @return a (managed) {@link ViatraQueryEngine} instance | ||
89 | * @since 1.4 | ||
90 | */ | ||
91 | public static ViatraQueryEngine on(QueryScope scope, ViatraQueryEngineOptions options) { | ||
92 | return ViatraQueryEngineManager.getInstance().getQueryEngine(scope, options); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Provides access to the internal base index component of the engine, responsible for keeping track of basic | ||
97 | * contents of the model. | ||
98 | * | ||
99 | * <p>If using an {@link EMFScope}, | ||
100 | * consider {@link EMFScope#extractUnderlyingEMFIndex(ViatraQueryEngine)} instead to access EMF-specific details. | ||
101 | * | ||
102 | * @return the baseIndex the NavigationHelper maintaining the base index | ||
103 | * @throws ViatraQueryRuntimeException | ||
104 | * if the base index could not be constructed | ||
105 | */ | ||
106 | public abstract IBaseIndex getBaseIndex(); | ||
107 | |||
108 | /** | ||
109 | * Access a pattern matcher based on a {@link IQuerySpecification}. | ||
110 | * Multiple calls will return the same matcher. | ||
111 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
112 | * @return a pattern matcher corresponding to the specification | ||
113 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
114 | */ | ||
115 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher(IQuerySpecification<Matcher> querySpecification); | ||
116 | |||
117 | /** | ||
118 | * Access a pattern matcher for the graph pattern with the given fully qualified name. | ||
119 | * Will succeed only if a query specification for this fully qualified name has been generated and registered. | ||
120 | * Multiple calls will return the same matcher unless the registered specification changes. | ||
121 | * | ||
122 | * @param patternFQN the fully qualified name of a VIATRA query specification | ||
123 | * @return a pattern matcher corresponding to the specification | ||
124 | * @throws ViatraQueryRuntimeException if the matcher could not be initialized | ||
125 | */ | ||
126 | public abstract ViatraQueryMatcher<? extends IPatternMatch> getMatcher(String patternFQN); | ||
127 | |||
128 | /** | ||
129 | * Access an existing pattern matcher based on a {@link IQuerySpecification}. | ||
130 | * @param querySpecification a {@link IQuerySpecification} that describes a VIATRA query specification | ||
131 | * @return a pattern matcher corresponding to the specification, <code>null</code> if a matcher does not exist yet. | ||
132 | */ | ||
133 | public abstract <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher(IQuerySpecification<Matcher> querySpecification); | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Access a copy of available {@link ViatraQueryMatcher} pattern matchers. | ||
138 | * @return a copy of the set of currently available pattern matchers registered on this engine instance | ||
139 | */ | ||
140 | public abstract Set<? extends ViatraQueryMatcher<? extends IPatternMatch>> getCurrentMatchers(); | ||
141 | |||
142 | public Set<IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>>> getRegisteredQuerySpecifications() { | ||
143 | return getCurrentMatchers().stream().map(ViatraQueryMatcher::getSpecification).collect(Collectors.toSet()); | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * @return the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
148 | */ | ||
149 | public abstract QueryScope getScope(); | ||
150 | |||
151 | public abstract void flushChanges(); | ||
152 | } | ||
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..ca709b02 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineManager.java | |||
@@ -0,0 +1,196 @@ | |||
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 static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
13 | |||
14 | import java.lang.ref.WeakReference; | ||
15 | import java.util.Collections; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | import java.util.WeakHashMap; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
22 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
23 | import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl; | ||
24 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
25 | |||
26 | /** | ||
27 | * Global registry of active VIATRA Query Engines. | ||
28 | * | ||
29 | * <p> | ||
30 | * Manages an {@link ViatraQueryEngine} for each model (more precisely scope), that is created on demand. Managed engines are shared between | ||
31 | * clients querying the same model. | ||
32 | * | ||
33 | * <p> | ||
34 | * It is also possible to create private, unmanaged engines that are not shared between clients. | ||
35 | * | ||
36 | * <p> | ||
37 | * Only weak references are retained on the managed engines. So if there are no other references to the matchers or the | ||
38 | * engine, they can eventually be GC'ed, and they won't block the model from being GC'ed either. | ||
39 | * | ||
40 | * | ||
41 | * @author Bergmann Gabor | ||
42 | * | ||
43 | */ | ||
44 | public class ViatraQueryEngineManager { | ||
45 | private static ViatraQueryEngineManager instance = new ViatraQueryEngineManager(); | ||
46 | |||
47 | |||
48 | /** | ||
49 | * @return the singleton instance | ||
50 | */ | ||
51 | public static ViatraQueryEngineManager getInstance() { | ||
52 | return instance; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * The engine manager keeps track of the managed engines via weak references only, so it will not keep unused | ||
57 | * managed engines from being garbage collected. Still, as long as the user keeps the model in memory, the | ||
58 | * associated managed engine will not be garbage collected, as it is expected to be strongly reachable from the | ||
59 | * model via the scope-specific base index or change notification listeners (see | ||
60 | * {@link BaseIndexListener#iqEngine}). | ||
61 | */ | ||
62 | Map<QueryScope, WeakReference<ViatraQueryEngineImpl>> engines; | ||
63 | |||
64 | ViatraQueryEngineManager() { | ||
65 | super(); | ||
66 | engines = new WeakHashMap<QueryScope, WeakReference<ViatraQueryEngineImpl>>(); | ||
67 | initializationListeners = new HashSet<ViatraQueryEngineInitializationListener>(); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Creates a managed VIATRA Query Engine at a given scope (e.g. an EMF Resource or ResourceSet, as in {@link EMFScope}) | ||
72 | * or retrieves an already existing one. Repeated invocations for a single model root will return the same engine. | ||
73 | * Consequently, the engine will be reused between different clients querying the same model, providing performance benefits. | ||
74 | * | ||
75 | * <p> | ||
76 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
77 | * | ||
78 | * @param scope | ||
79 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
80 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
81 | * @return a new or previously existing engine | ||
82 | */ | ||
83 | public ViatraQueryEngine getQueryEngine(QueryScope scope) { | ||
84 | return getQueryEngine(scope, ViatraQueryEngineOptions.getDefault()); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Creates a managed VIATRA Query Engine at a given scope (e.g. an EMF Resource or ResourceSet, as in {@link EMFScope}) | ||
89 | * or retrieves an already existing one. Repeated invocations for a single model root will return the same engine. | ||
90 | * Consequently, the engine will be reused between different clients querying the same model, providing performance benefits. | ||
91 | * | ||
92 | * <p> | ||
93 | * The match set of any patterns will be incrementally refreshed upon updates from this scope. | ||
94 | * | ||
95 | * @param scope | ||
96 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
97 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
98 | * @return a new or previously existing engine | ||
99 | * @since 1.4 | ||
100 | */ | ||
101 | public ViatraQueryEngine getQueryEngine(QueryScope scope, ViatraQueryEngineOptions options) { | ||
102 | ViatraQueryEngineImpl engine = getEngineInternal(scope); | ||
103 | if (engine == null) { | ||
104 | engine = new ViatraQueryEngineImpl(this, scope, options); | ||
105 | engines.put(scope, new WeakReference<ViatraQueryEngineImpl>(engine)); | ||
106 | notifyInitializationListeners(engine); | ||
107 | } | ||
108 | return engine; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Retrieves an already existing managed VIATRA Query Engine. | ||
113 | * | ||
114 | * @param scope | ||
115 | * the scope of query evaluation; the definition of the set of model elements that this engine is operates on. | ||
116 | * Provide e.g. a {@link EMFScope} for evaluating queries on an EMF model. | ||
117 | * @return a previously existing engine, or null if no engine is active for the given EMF model root | ||
118 | */ | ||
119 | public ViatraQueryEngine getQueryEngineIfExists(QueryScope scope) { | ||
120 | return getEngineInternal(scope); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Collects all {@link ViatraQueryEngine} instances that still exist. | ||
125 | * | ||
126 | * @return set of engines if there is any, otherwise EMTPY_SET | ||
127 | */ | ||
128 | public Set<ViatraQueryEngine> getExistingQueryEngines(){ | ||
129 | Set<ViatraQueryEngine> existingEngines = null; | ||
130 | for (WeakReference<ViatraQueryEngineImpl> engineRef : engines.values()) { | ||
131 | AdvancedViatraQueryEngine engine = engineRef == null ? null : engineRef.get(); | ||
132 | if(existingEngines == null) { | ||
133 | existingEngines = new HashSet<>(); | ||
134 | } | ||
135 | existingEngines.add(engine); | ||
136 | } | ||
137 | if(existingEngines == null) { | ||
138 | existingEngines = Collections.emptySet(); | ||
139 | } | ||
140 | return existingEngines; | ||
141 | } | ||
142 | |||
143 | private final Set<ViatraQueryEngineInitializationListener> initializationListeners; | ||
144 | |||
145 | /** | ||
146 | * Registers a listener for new engine initialization. | ||
147 | * | ||
148 | * <p/> For removal, use {@link #removeQueryEngineInitializationListener} | ||
149 | * | ||
150 | * @param listener the listener to register | ||
151 | */ | ||
152 | public void addQueryEngineInitializationListener(ViatraQueryEngineInitializationListener listener) { | ||
153 | checkArgument(listener != null, "Cannot add null listener!"); | ||
154 | initializationListeners.add(listener); | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * Removes a registered listener added with {@link #addQueryEngineInitializationListener} | ||
159 | * | ||
160 | * @param listener | ||
161 | */ | ||
162 | public void removeQueryEngineInitializationListener(ViatraQueryEngineInitializationListener listener) { | ||
163 | checkArgument(listener != null, "Cannot remove null listener!"); | ||
164 | initializationListeners.remove(listener); | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * Notifies listeners that a new engine has been initialized. | ||
169 | * | ||
170 | * @param engine the initialized engine | ||
171 | */ | ||
172 | protected void notifyInitializationListeners(AdvancedViatraQueryEngine engine) { | ||
173 | try { | ||
174 | if (!initializationListeners.isEmpty()) { | ||
175 | for (ViatraQueryEngineInitializationListener listener : new HashSet<>(initializationListeners)) { | ||
176 | listener.engineInitialized(engine); | ||
177 | } | ||
178 | } | ||
179 | } catch (Exception ex) { | ||
180 | ViatraQueryLoggingUtil.getLogger(getClass()).fatal( | ||
181 | "VIATRA Query Engine Manager encountered an error in delivering notifications" | ||
182 | + " about engine initialization. ", ex); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @param emfRoot | ||
188 | * @return | ||
189 | */ | ||
190 | private ViatraQueryEngineImpl getEngineInternal(QueryScope scope) { | ||
191 | final WeakReference<ViatraQueryEngineImpl> engineRef = engines.get(scope); | ||
192 | ViatraQueryEngineImpl engine = engineRef == null ? null : engineRef.get(); | ||
193 | return engine; | ||
194 | } | ||
195 | |||
196 | } | ||
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/BaseGeneratedEMFPQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFPQuery.java new file mode 100644 index 00000000..c1fb0622 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFPQuery.java | |||
@@ -0,0 +1,110 @@ | |||
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.api.impl; | ||
10 | |||
11 | import org.eclipse.emf.ecore.EClass; | ||
12 | import org.eclipse.emf.ecore.EClassifier; | ||
13 | import org.eclipse.emf.ecore.EEnum; | ||
14 | import org.eclipse.emf.ecore.EEnumLiteral; | ||
15 | import org.eclipse.emf.ecore.EPackage; | ||
16 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
17 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.queries.BasePQuery; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; | ||
21 | |||
22 | /** | ||
23 | * Common superclass for EMF-based generated PQueries. | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public abstract class BaseGeneratedEMFPQuery extends BasePQuery { | ||
28 | |||
29 | public BaseGeneratedEMFPQuery() { | ||
30 | this(PVisibility.PUBLIC); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * @since 2.0 | ||
35 | */ | ||
36 | public BaseGeneratedEMFPQuery(PVisibility visibility) { | ||
37 | super(visibility); | ||
38 | } | ||
39 | |||
40 | protected QueryInitializationException processDependencyException(ViatraQueryException ex) { | ||
41 | if (ex.getCause() instanceof QueryInitializationException) | ||
42 | return (QueryInitializationException) ex.getCause(); | ||
43 | return new QueryInitializationException( | ||
44 | "Failed to initialize external dependencies of query specification - see 'caused by' for details.", | ||
45 | null, "Problem with query dependencies.", this, ex); | ||
46 | } | ||
47 | |||
48 | protected EClassifier getClassifierLiteral(String packageUri, String classifierName) { | ||
49 | EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(packageUri); | ||
50 | if (ePackage == null) | ||
51 | throw new QueryInitializationException( | ||
52 | "Query refers to EPackage {1} not found in EPackage Registry.", | ||
53 | new String[]{packageUri}, | ||
54 | "Query refers to missing EPackage.", this); | ||
55 | EClassifier literal = ePackage.getEClassifier(classifierName); | ||
56 | if (literal == null) | ||
57 | throw new QueryInitializationException( | ||
58 | "Query refers to classifier {1} not found in EPackage {2}.", | ||
59 | new String[]{classifierName, packageUri}, | ||
60 | "Query refers to missing type in EPackage.", this); | ||
61 | return literal; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * For parameter type retrieval only. | ||
66 | * | ||
67 | * <p>If parameter type declaration is erroneous, we still get a working parameter list (without the type declaration); | ||
68 | * the exception will be thrown again later when the body is processed. | ||
69 | */ | ||
70 | protected EClassifier getClassifierLiteralSafe(String packageURI, String classifierName) { | ||
71 | try { | ||
72 | return getClassifierLiteral(packageURI, classifierName); | ||
73 | } catch (QueryInitializationException e) { | ||
74 | return null; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | protected EStructuralFeature getFeatureLiteral(String packageUri, String className, String featureName) { | ||
79 | EClassifier container = getClassifierLiteral(packageUri, className); | ||
80 | if (! (container instanceof EClass)) | ||
81 | throw new QueryInitializationException( | ||
82 | "Query refers to EClass {1} in EPackage {2} which turned out not be an EClass.", | ||
83 | new String[]{className, packageUri}, | ||
84 | "Query refers to missing EClass.", this); | ||
85 | EStructuralFeature feature = ((EClass)container).getEStructuralFeature(featureName); | ||
86 | if (feature == null) | ||
87 | throw new QueryInitializationException( | ||
88 | "Query refers to feature {1} not found in EClass {2}.", | ||
89 | new String[]{featureName, className}, | ||
90 | "Query refers to missing feature.", this); | ||
91 | return feature; | ||
92 | } | ||
93 | |||
94 | protected EEnumLiteral getEnumLiteral(String packageUri, String enumName, String literalName) { | ||
95 | EClassifier enumContainer = getClassifierLiteral(packageUri, enumName); | ||
96 | if (! (enumContainer instanceof EEnum)) | ||
97 | throw new QueryInitializationException( | ||
98 | "Query refers to EEnum {1} in EPackage {2} which turned out not be an EEnum.", | ||
99 | new String[]{enumName, packageUri}, | ||
100 | "Query refers to missing enumeration type.", this); | ||
101 | EEnumLiteral literal = ((EEnum)enumContainer).getEEnumLiteral(literalName); | ||
102 | if (literal == null) | ||
103 | throw new QueryInitializationException( | ||
104 | "Query refers to enumeration literal {1} not found in EEnum {2}.", | ||
105 | new String[]{literalName, enumName}, | ||
106 | "Query refers to missing enumeration literal.", this); | ||
107 | return literal; | ||
108 | } | ||
109 | |||
110 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecification.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecification.java new file mode 100644 index 00000000..9956983d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecification.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.api.impl; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
13 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
15 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * Provides common functionality of pattern-specific generated query specifications over the EMF scope. | ||
20 | * | ||
21 | * @author Bergmann Gábor | ||
22 | * @author Mark Czotter | ||
23 | */ | ||
24 | public abstract class BaseGeneratedEMFQuerySpecification<Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> extends | ||
25 | BaseQuerySpecification<Matcher> { | ||
26 | |||
27 | |||
28 | /** | ||
29 | * Instantiates query specification for the given internal query representation. | ||
30 | */ | ||
31 | public BaseGeneratedEMFQuerySpecification(PQuery wrappedPQuery) { | ||
32 | super(wrappedPQuery); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Class<? extends QueryScope> getPreferredScopeClass() { | ||
37 | return EMFScope.class; | ||
38 | } | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecificationWithGenericMatcher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecificationWithGenericMatcher.java new file mode 100644 index 00000000..949dd112 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecificationWithGenericMatcher.java | |||
@@ -0,0 +1,58 @@ | |||
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.api.impl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.GenericPatternMatch; | ||
12 | import tools.refinery.viatra.runtime.api.GenericPatternMatcher; | ||
13 | import tools.refinery.viatra.runtime.api.GenericQuerySpecification; | ||
14 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
15 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
16 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | |||
19 | /** | ||
20 | * Provides common functionality of pattern-specific generated query specifications for without generated | ||
21 | * pattern-specific match and matcher classes, including private patterns. | ||
22 | * | ||
23 | * @since 1.7 | ||
24 | * | ||
25 | */ | ||
26 | public abstract class BaseGeneratedEMFQuerySpecificationWithGenericMatcher | ||
27 | extends GenericQuerySpecification<GenericPatternMatcher> { | ||
28 | |||
29 | public BaseGeneratedEMFQuerySpecificationWithGenericMatcher(PQuery wrappedPQuery) { | ||
30 | super(wrappedPQuery); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public Class<? extends QueryScope> getPreferredScopeClass() { | ||
35 | return EMFScope.class; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | protected GenericPatternMatcher instantiate(final ViatraQueryEngine engine) { | ||
40 | return defaultInstantiate(engine); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public GenericPatternMatcher instantiate() { | ||
45 | return new GenericPatternMatcher(this); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public GenericPatternMatch newEmptyMatch() { | ||
50 | return GenericPatternMatch.newEmptyMatch(this); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public GenericPatternMatch newMatch(final Object... parameters) { | ||
55 | return GenericPatternMatch.newMatch(this, parameters); | ||
56 | } | ||
57 | |||
58 | } \ No newline at end of file | ||
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..7690daf6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BasePatternMatch.java | |||
@@ -0,0 +1,115 @@ | |||
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.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | |||
16 | import org.eclipse.emf.ecore.EObject; | ||
17 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
18 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
19 | |||
20 | /** | ||
21 | * Base implementation of IPatternMatch. | ||
22 | * | ||
23 | * @author Bergmann Gábor | ||
24 | * | ||
25 | */ | ||
26 | public abstract class BasePatternMatch implements IPatternMatch { | ||
27 | |||
28 | @SafeVarargs | ||
29 | protected static <T> List<T> makeImmutableList(T... elements) { | ||
30 | return Collections.unmodifiableList(Arrays.asList(elements)); | ||
31 | } | ||
32 | |||
33 | public static String prettyPrintValue(Object o) { | ||
34 | if (o == null) { | ||
35 | return "(null)"; | ||
36 | } | ||
37 | String name = prettyPrintFeature(o, "name"); | ||
38 | if (name != null) { | ||
39 | return name; | ||
40 | } | ||
41 | /* | ||
42 | * if (o instanceof EObject) { EStructuralFeature feature = ((EObject)o).eClass().getEStructuralFeature("name"); | ||
43 | * if (feature != null) { Object name = ((EObject)o).eGet(feature); if (name != null) return name.toString(); } | ||
44 | * } | ||
45 | */ | ||
46 | return o.toString(); | ||
47 | } | ||
48 | |||
49 | public static String prettyPrintFeature(Object o, String featureName) { | ||
50 | if (o instanceof EObject) { | ||
51 | EStructuralFeature feature = ((EObject) o).eClass().getEStructuralFeature(featureName); | ||
52 | if (feature != null) { | ||
53 | Object value = ((EObject) o).eGet(feature); | ||
54 | if (value != null) { | ||
55 | return value.toString(); | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | return null; | ||
60 | } | ||
61 | |||
62 | // TODO performance can be improved here somewhat | ||
63 | |||
64 | @Override | ||
65 | public Object get(int position) { | ||
66 | if (position >= 0 && position < parameterNames().size()) | ||
67 | return get(parameterNames().get(position)); | ||
68 | else | ||
69 | return null; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean set(int position, Object newValue) { | ||
74 | if (!isMutable()) throw new UnsupportedOperationException(); | ||
75 | if (position >= 0 && position < parameterNames().size()) { | ||
76 | return set(parameterNames().get(position), newValue); | ||
77 | } else { | ||
78 | return false; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public String toString() { | ||
84 | return "Match<" + patternName() + ">{" + prettyPrint() + "}"; | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public boolean isCompatibleWith(IPatternMatch other) { | ||
89 | if(other == null) { | ||
90 | return true; | ||
91 | } | ||
92 | // we assume that the pattern is set for this match! | ||
93 | if (!specification().equals(other.specification())) { | ||
94 | return false; | ||
95 | } | ||
96 | for (int i = 0; i < parameterNames().size(); i++) { | ||
97 | Object value = get(i); | ||
98 | Object otherValue = other.get(i); | ||
99 | if(value != null && otherValue != null && !value.equals(otherValue)) { | ||
100 | return false; | ||
101 | } | ||
102 | } | ||
103 | return true; | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public String patternName() { | ||
108 | return specification().getFullyQualifiedName(); | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | public List<String> parameterNames() { | ||
113 | return specification().getParameterNames(); | ||
114 | } | ||
115 | } | ||
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/impl/RunOnceQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/RunOnceQueryEngine.java new file mode 100644 index 00000000..a97dea5d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/RunOnceQueryEngine.java | |||
@@ -0,0 +1,140 @@ | |||
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.impl; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import org.eclipse.emf.common.notify.Notifier; | ||
14 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | ||
15 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
16 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
17 | import tools.refinery.viatra.runtime.api.IRunOnceQueryEngine; | ||
18 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
19 | import tools.refinery.viatra.runtime.api.ViatraQueryMatcher; | ||
20 | import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener; | ||
21 | import tools.refinery.viatra.runtime.base.api.BaseIndexOptions; | ||
22 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
23 | |||
24 | /** | ||
25 | * Run-once query engines can be used to retrieve the current match set of query specifications | ||
26 | * in a given scope. The engine is initialized with a {@link Notifier} as scope and a base index options | ||
27 | * that specifically allows traversing derived features that are not well-behaving. | ||
28 | * | ||
29 | * @author Abel Hegedus | ||
30 | * | ||
31 | */ | ||
32 | public class RunOnceQueryEngine implements IRunOnceQueryEngine { | ||
33 | |||
34 | /** | ||
35 | * If the model changes, we know that a resampling is required. | ||
36 | * | ||
37 | * @author Abel Hegedus | ||
38 | * | ||
39 | */ | ||
40 | private final class RunOnceSamplingModelUpdateListener implements ViatraQueryModelUpdateListener { | ||
41 | @Override | ||
42 | public void notifyChanged(ChangeLevel changeLevel) { | ||
43 | // any model change may require re-sampling | ||
44 | reSamplingNeeded = true; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public ChangeLevel getLevel() { | ||
49 | return ChangeLevel.MODEL; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * Override the default base index options to allow traversing and indexing derived features | ||
55 | * that would be problematic in incremental evaluation. | ||
56 | * | ||
57 | * @author Abel Hegedus | ||
58 | * | ||
59 | */ | ||
60 | private static final class RunOnceBaseIndexOptions extends BaseIndexOptions { | ||
61 | |||
62 | public RunOnceBaseIndexOptions() { | ||
63 | this.traverseOnlyWellBehavingDerivedFeatures = false; | ||
64 | } | ||
65 | |||
66 | } | ||
67 | |||
68 | /** | ||
69 | * The scope of the engine that is used when creating one-time {@link ViatraQueryEngine}s. | ||
70 | */ | ||
71 | private Notifier notifier; | ||
72 | /** | ||
73 | * The options that are used for initializing the {@link ViatraQueryEngine}. | ||
74 | */ | ||
75 | private RunOnceBaseIndexOptions baseIndexOptions; | ||
76 | private AdvancedViatraQueryEngine engine; | ||
77 | private boolean reSamplingNeeded = false; | ||
78 | protected boolean samplingMode = false; | ||
79 | private RunOnceSamplingModelUpdateListener modelUpdateListener; | ||
80 | |||
81 | /** | ||
82 | * Creates a run-once query engine on the given notifier. | ||
83 | */ | ||
84 | public RunOnceQueryEngine(Notifier notifier) { | ||
85 | this.notifier = notifier; | ||
86 | this.baseIndexOptions = new RunOnceBaseIndexOptions(); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public <Match extends IPatternMatch> Collection<Match> getAllMatches( | ||
91 | IQuerySpecification<? extends ViatraQueryMatcher<Match>> querySpecification) { | ||
92 | |||
93 | if(samplingMode && reSamplingNeeded && engine != null) { | ||
94 | // engine exists from earlier, but may need resampling if model changed | ||
95 | engine.getBaseIndex().resampleDerivedFeatures(); | ||
96 | } else { | ||
97 | // create new engine if it doesn't exists | ||
98 | //TODO correct scope handling | ||
99 | engine = AdvancedViatraQueryEngine.createUnmanagedEngine(new EMFScope(notifier, baseIndexOptions)); | ||
100 | } | ||
101 | ViatraQueryMatcher<Match> matcher = engine.getMatcher(querySpecification); | ||
102 | Collection<Match> allMatches = matcher.getAllMatches(); | ||
103 | if(samplingMode) { | ||
104 | engine.addModelUpdateListener(modelUpdateListener); | ||
105 | } else { | ||
106 | engine.dispose(); | ||
107 | engine = null; | ||
108 | } | ||
109 | return allMatches; | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public BaseIndexOptions getBaseIndexOptions() { | ||
114 | return baseIndexOptions; | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public Notifier getScope() { | ||
119 | return notifier; | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public void setAutomaticResampling(boolean automaticResampling) { | ||
124 | samplingMode = automaticResampling; | ||
125 | if(automaticResampling) { | ||
126 | if (modelUpdateListener == null) { | ||
127 | modelUpdateListener = new RunOnceSamplingModelUpdateListener(); | ||
128 | } | ||
129 | } else if(engine != null) { | ||
130 | engine.dispose(); | ||
131 | engine = null; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public void resampleOnNextCall() { | ||
137 | reSamplingNeeded = true; | ||
138 | } | ||
139 | |||
140 | } | ||
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..d144bba6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IIndexingErrorListener.java | |||
@@ -0,0 +1,25 @@ | |||
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 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
12 | |||
13 | /** | ||
14 | * | ||
15 | * This interface contains callbacks for various internal errors from the {@link NavigationHelper base index}. | ||
16 | * | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 0.9 | ||
19 | * | ||
20 | */ | ||
21 | public interface IIndexingErrorListener { | ||
22 | |||
23 | void error(String description, Throwable t); | ||
24 | void fatal(String description, Throwable t); | ||
25 | } | ||
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/emf/DynamicEMFQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/DynamicEMFQueryRuntimeContext.java new file mode 100644 index 00000000..a6da213e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/DynamicEMFQueryRuntimeContext.java | |||
@@ -0,0 +1,47 @@ | |||
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.emf; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
15 | |||
16 | /** | ||
17 | * In dynamic EMF mode, we need to make sure that EEnum literal constants and values returned by eval() expressions | ||
18 | * are canonicalized in the same way as enum literals from the EMF model. | ||
19 | * | ||
20 | * <p> This canonicalization is a one-way mapping, so | ||
21 | * {@link #unwrapElement(Object)} and {@link #unwrapTuple(Object)} remain NOPs. | ||
22 | * | ||
23 | * @author Bergmann Gabor | ||
24 | * | ||
25 | */ | ||
26 | public class DynamicEMFQueryRuntimeContext extends EMFQueryRuntimeContext { | ||
27 | |||
28 | public DynamicEMFQueryRuntimeContext(NavigationHelper baseIndex, Logger logger, EMFScope emfScope) { | ||
29 | super(baseIndex, logger, emfScope); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public Object wrapElement(Object externalElement) { | ||
34 | return baseIndex.toCanonicalValueRepresentation(externalElement); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Tuple wrapTuple(Tuple externalElements) { | ||
39 | Object[] elements = externalElements.getElements(); | ||
40 | for (int i=0; i< elements.length; ++i) | ||
41 | elements[i] = wrapElement(elements[i]); | ||
42 | return Tuples.flatTupleOf(elements); | ||
43 | } | ||
44 | |||
45 | |||
46 | |||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFBaseIndexWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFBaseIndexWrapper.java new file mode 100644 index 00000000..433c5f72 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFBaseIndexWrapper.java | |||
@@ -0,0 +1,160 @@ | |||
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.emf; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.Map; | ||
14 | import java.util.concurrent.Callable; | ||
15 | |||
16 | import org.eclipse.emf.common.notify.Notification; | ||
17 | import org.eclipse.emf.ecore.EObject; | ||
18 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
19 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
20 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
21 | import tools.refinery.viatra.runtime.api.scope.IInstanceObserver; | ||
22 | import tools.refinery.viatra.runtime.api.scope.ViatraBaseIndexChangeListener; | ||
23 | import tools.refinery.viatra.runtime.base.api.EMFBaseIndexChangeListener; | ||
24 | import tools.refinery.viatra.runtime.base.api.IEMFIndexingErrorListener; | ||
25 | import tools.refinery.viatra.runtime.base.api.LightweightEObjectObserver; | ||
26 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
27 | |||
28 | /** | ||
29 | * Wraps the EMF base index into the IBaseIndex interface. | ||
30 | * @author Bergmann Gabor | ||
31 | * | ||
32 | */ | ||
33 | public class EMFBaseIndexWrapper implements IBaseIndex { | ||
34 | |||
35 | private final NavigationHelper navigationHelper; | ||
36 | /** | ||
37 | * @return the underlying index object | ||
38 | */ | ||
39 | public NavigationHelper getNavigationHelper() { | ||
40 | return navigationHelper; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * @param navigationHelper | ||
45 | */ | ||
46 | public EMFBaseIndexWrapper(NavigationHelper navigationHelper) { | ||
47 | this.navigationHelper = navigationHelper; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void resampleDerivedFeatures() { | ||
52 | navigationHelper.resampleDerivedFeatures(); | ||
53 | } | ||
54 | |||
55 | |||
56 | @Override | ||
57 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
58 | return navigationHelper.coalesceTraversals(callable); | ||
59 | } | ||
60 | |||
61 | Map<IIndexingErrorListener, IEMFIndexingErrorListener> indexErrorListeners = | ||
62 | new HashMap<IIndexingErrorListener, IEMFIndexingErrorListener>(); | ||
63 | @Override | ||
64 | public boolean addIndexingErrorListener(final IIndexingErrorListener listener) { | ||
65 | if (indexErrorListeners.containsKey(listener)) return false; | ||
66 | IEMFIndexingErrorListener emfListener = new IEMFIndexingErrorListener() { | ||
67 | @Override | ||
68 | public void fatal(String description, Throwable t) { | ||
69 | listener.fatal(description, t); | ||
70 | } | ||
71 | @Override | ||
72 | public void error(String description, Throwable t) { | ||
73 | listener.error(description, t); | ||
74 | } | ||
75 | }; | ||
76 | indexErrorListeners.put(listener, emfListener); | ||
77 | return navigationHelper.addIndexingErrorListener(emfListener); | ||
78 | } | ||
79 | @Override | ||
80 | public boolean removeIndexingErrorListener(IIndexingErrorListener listener) { | ||
81 | if (!indexErrorListeners.containsKey(listener)) return false; | ||
82 | return navigationHelper.removeIndexingErrorListener(indexErrorListeners.remove(listener)); | ||
83 | } | ||
84 | |||
85 | |||
86 | Map<ViatraBaseIndexChangeListener, EMFBaseIndexChangeListener> indexChangeListeners = | ||
87 | new HashMap<ViatraBaseIndexChangeListener, EMFBaseIndexChangeListener>(); | ||
88 | @Override | ||
89 | public void addBaseIndexChangeListener(final ViatraBaseIndexChangeListener listener) { | ||
90 | EMFBaseIndexChangeListener emfListener = new EMFBaseIndexChangeListener() { | ||
91 | @Override | ||
92 | public boolean onlyOnIndexChange() { | ||
93 | return listener.onlyOnIndexChange(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void notifyChanged(boolean indexChanged) { | ||
98 | listener.notifyChanged(indexChanged); | ||
99 | } | ||
100 | }; | ||
101 | indexChangeListeners.put(listener, emfListener); | ||
102 | navigationHelper.addBaseIndexChangeListener(emfListener); | ||
103 | } | ||
104 | @Override | ||
105 | public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { | ||
106 | final EMFBaseIndexChangeListener cListener = indexChangeListeners.remove(listener); | ||
107 | if (cListener != null) | ||
108 | navigationHelper.removeBaseIndexChangeListener(cListener); | ||
109 | } | ||
110 | |||
111 | Map<IInstanceObserver, EObjectObserver> instanceObservers = | ||
112 | new HashMap<IInstanceObserver, EObjectObserver>(); | ||
113 | @Override | ||
114 | public boolean addInstanceObserver(final IInstanceObserver observer, | ||
115 | Object observedObject) { | ||
116 | if (observedObject instanceof EObject) { | ||
117 | EObjectObserver emfObserver = instanceObservers.computeIfAbsent(observer, EObjectObserver::new); | ||
118 | boolean success = | ||
119 | navigationHelper.addLightweightEObjectObserver(emfObserver, (EObject) observedObject); | ||
120 | if (success) emfObserver.usageCount++; | ||
121 | return success; | ||
122 | } else return false; | ||
123 | } | ||
124 | @Override | ||
125 | public boolean removeInstanceObserver(IInstanceObserver observer, | ||
126 | Object observedObject) { | ||
127 | if (observedObject instanceof EObject) { | ||
128 | EObjectObserver emfObserver = instanceObservers.get(observer); | ||
129 | if (emfObserver == null) | ||
130 | return false; | ||
131 | boolean success = | ||
132 | navigationHelper.removeLightweightEObjectObserver(emfObserver, (EObject)observedObject); | ||
133 | if (success) | ||
134 | if (0 == --emfObserver.usageCount) | ||
135 | instanceObservers.remove(observer); | ||
136 | return success; | ||
137 | } else return false; | ||
138 | } | ||
139 | private static class EObjectObserver implements LightweightEObjectObserver { | ||
140 | /** | ||
141 | * | ||
142 | */ | ||
143 | private final IInstanceObserver observer; | ||
144 | int usageCount = 0; | ||
145 | |||
146 | /** | ||
147 | * @param observer | ||
148 | */ | ||
149 | private EObjectObserver(IInstanceObserver observer) { | ||
150 | this.observer = observer; | ||
151 | } | ||
152 | |||
153 | @Override | ||
154 | public void notifyFeatureChanged(EObject host, | ||
155 | EStructuralFeature feature, Notification notification) { | ||
156 | observer.notifyBinaryChanged(host, feature); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFEngineContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFEngineContext.java new file mode 100644 index 00000000..5fe9e23a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFEngineContext.java | |||
@@ -0,0 +1,110 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Denes Harmath, 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.emf; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import org.eclipse.emf.common.notify.Notifier; | ||
13 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
14 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
15 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
16 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
17 | import tools.refinery.viatra.runtime.base.api.ViatraBaseFactory; | ||
18 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
19 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
21 | |||
22 | /** | ||
23 | * Implements an engine context on EMF models. | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | class EMFEngineContext implements IEngineContext { | ||
28 | |||
29 | private final EMFScope emfScope; | ||
30 | ViatraQueryEngine engine; | ||
31 | Logger logger; | ||
32 | NavigationHelper navHelper; | ||
33 | IBaseIndex baseIndex; | ||
34 | IIndexingErrorListener taintListener; | ||
35 | private EMFQueryRuntimeContext runtimeContext; | ||
36 | |||
37 | public EMFEngineContext(EMFScope emfScope, ViatraQueryEngine engine, IIndexingErrorListener taintListener, Logger logger) { | ||
38 | this.emfScope = emfScope; | ||
39 | this.engine = engine; | ||
40 | this.logger = logger; | ||
41 | this.taintListener = taintListener; | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * @throws ViatraQueryRuntimeException thrown if the navigation helper cannot be initialized | ||
46 | */ | ||
47 | public NavigationHelper getNavHelper() { | ||
48 | return getNavHelper(true); | ||
49 | } | ||
50 | |||
51 | private NavigationHelper getNavHelper(boolean ensureInitialized) { | ||
52 | if (navHelper == null) { | ||
53 | // sync to avoid crazy compiler reordering which would matter if derived features use VIATRA and call this | ||
54 | // reentrantly | ||
55 | synchronized (this) { | ||
56 | navHelper = ViatraBaseFactory.getInstance().createNavigationHelper(null, this.emfScope.getOptions(), | ||
57 | logger); | ||
58 | getBaseIndex().addIndexingErrorListener(taintListener); | ||
59 | } | ||
60 | |||
61 | if (ensureInitialized) { | ||
62 | ensureIndexLoaded(); | ||
63 | } | ||
64 | |||
65 | } | ||
66 | return navHelper; | ||
67 | } | ||
68 | |||
69 | private void ensureIndexLoaded() { | ||
70 | for (Notifier scopeRoot : this.emfScope.getScopeRoots()) { | ||
71 | navHelper.addRoot(scopeRoot); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public IQueryRuntimeContext getQueryRuntimeContext() { | ||
77 | NavigationHelper nh = getNavHelper(false); | ||
78 | if (runtimeContext == null) { | ||
79 | runtimeContext = | ||
80 | emfScope.getOptions().isDynamicEMFMode() ? | ||
81 | new DynamicEMFQueryRuntimeContext(nh, logger, emfScope) : | ||
82 | new EMFQueryRuntimeContext(nh, logger, emfScope); | ||
83 | |||
84 | ensureIndexLoaded(); | ||
85 | } | ||
86 | |||
87 | return runtimeContext; | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public void dispose() { | ||
92 | if (runtimeContext != null) runtimeContext.dispose(); | ||
93 | if (navHelper != null) navHelper.dispose(); | ||
94 | |||
95 | this.baseIndex = null; | ||
96 | this.engine = null; | ||
97 | this.logger = null; | ||
98 | this.navHelper = null; | ||
99 | } | ||
100 | |||
101 | |||
102 | @Override | ||
103 | public IBaseIndex getBaseIndex() { | ||
104 | if (baseIndex == null) { | ||
105 | final NavigationHelper navigationHelper = getNavHelper(); | ||
106 | baseIndex = new EMFBaseIndexWrapper(navigationHelper); | ||
107 | } | ||
108 | return baseIndex; | ||
109 | } | ||
110 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryMetaContext.java new file mode 100644 index 00000000..c316d308 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryMetaContext.java | |||
@@ -0,0 +1,405 @@ | |||
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.emf; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Comparator; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | |||
20 | import org.eclipse.emf.common.util.EList; | ||
21 | import org.eclipse.emf.ecore.EAttribute; | ||
22 | import org.eclipse.emf.ecore.EClass; | ||
23 | import org.eclipse.emf.ecore.EClassifier; | ||
24 | import org.eclipse.emf.ecore.EDataType; | ||
25 | import org.eclipse.emf.ecore.EObject; | ||
26 | import org.eclipse.emf.ecore.EReference; | ||
27 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
28 | import org.eclipse.emf.ecore.EcorePackage; | ||
29 | import tools.refinery.viatra.runtime.emf.types.BaseEMFTypeKey; | ||
30 | import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey; | ||
31 | import tools.refinery.viatra.runtime.emf.types.EClassUnscopedTransitiveInstancesKey; | ||
32 | import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey; | ||
33 | import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey; | ||
34 | import tools.refinery.viatra.runtime.matchers.context.AbstractQueryMetaContext; | ||
35 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
36 | import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; | ||
37 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
38 | |||
39 | /** | ||
40 | * The meta context information for EMF scopes. | ||
41 | * | ||
42 | * <p> The runtime context may specialize answers with a given scope. | ||
43 | * In a static context, a conservative default version ({@link #DEFAULT}) can be used instead. | ||
44 | * | ||
45 | * <p> TODO generics? | ||
46 | * @author Bergmann Gabor | ||
47 | * | ||
48 | */ | ||
49 | public final class EMFQueryMetaContext extends AbstractQueryMetaContext { | ||
50 | |||
51 | /** | ||
52 | * Default static instance that only makes conservative assumptions that are valid for any {@link EMFScope} (but not if objects are used). | ||
53 | * @since 1.6 | ||
54 | */ | ||
55 | public static final EMFQueryMetaContext DEFAULT = new EMFQueryMetaContext(false, true, UnscopedTypeSupport.EMIT_ALWAYS); | ||
56 | |||
57 | /** | ||
58 | * Default static instance that only makes conservative assumptions that are valid for any scope, even with surrogate objects. | ||
59 | * Unscoped types are used for inference, but not emitted as replacement candidates, as they cannot be checked at runtime. | ||
60 | * @since 2.1 | ||
61 | */ | ||
62 | public static final EMFQueryMetaContext DEFAULT_SURROGATE = new EMFQueryMetaContext(false, true, UnscopedTypeSupport.EMIT_EXCEPT_AS_WEAKENED_REPLACEMENT); | ||
63 | |||
64 | |||
65 | private static final EClass EOBJECT_CLASS = | ||
66 | EcorePackage.eINSTANCE.getEObject(); | ||
67 | private static final EClassTransitiveInstancesKey EOBJECT_SCOPED_KEY = | ||
68 | new EClassTransitiveInstancesKey(EOBJECT_CLASS); | ||
69 | private static final EClassUnscopedTransitiveInstancesKey EOBJECT_UNSCOPED_KEY = | ||
70 | new EClassUnscopedTransitiveInstancesKey(EOBJECT_CLASS); | ||
71 | |||
72 | private boolean assumeNonDangling; | ||
73 | private boolean subResourceScopeSplit; | ||
74 | private UnscopedTypeSupport emitUnscopedEClassTypes; | ||
75 | |||
76 | private enum UnscopedTypeSupport { | ||
77 | EMIT_ALWAYS, | ||
78 | EMIT_EXCEPT_AS_WEAKENED_REPLACEMENT, | ||
79 | EMIT_NEVER | ||
80 | } | ||
81 | |||
82 | |||
83 | /** | ||
84 | * Instantiates a specialized meta information that is aware of scope-specific details. | ||
85 | * Note that this API is not stable and thus non-public. | ||
86 | * | ||
87 | * @param assumeNonDangling assumes that all cross-references are non-dangling (do not lead out of scope), no matter what | ||
88 | * @param subResourceScopeSplit the scope granularity may be finer than resource-level, i.e. proxy-non-resolving references can lead out of scope | ||
89 | * @param emitUnscopedEClassTypes if requested, the metacontext will suppress unscoped input keys; this is recommended if surrogates are used instead of EObjects | ||
90 | */ | ||
91 | EMFQueryMetaContext(boolean assumeNonDangling, boolean subResourceScopeSplit, UnscopedTypeSupport emitUnscopedEClassTypes) { | ||
92 | this.assumeNonDangling = assumeNonDangling; | ||
93 | this.subResourceScopeSplit = subResourceScopeSplit; | ||
94 | this.emitUnscopedEClassTypes = emitUnscopedEClassTypes; | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Instantiates a specialized meta information that is aware of scope-specific details. | ||
99 | * @since 2.1 | ||
100 | */ | ||
101 | public EMFQueryMetaContext(EMFScope scope) { | ||
102 | this(scope.getOptions().isDanglingFreeAssumption(), | ||
103 | scope.getScopeRoots().size()==1 && scope.getScopeRoots().iterator().next() instanceof EObject, | ||
104 | UnscopedTypeSupport.EMIT_ALWAYS); | ||
105 | } | ||
106 | |||
107 | |||
108 | @Override | ||
109 | public boolean isEnumerable(IInputKey key) { | ||
110 | ensureValidKey(key); | ||
111 | return key.isEnumerable(); | ||
112 | // if (key instanceof JavaTransitiveInstancesKey) | ||
113 | // return false; | ||
114 | // else | ||
115 | // return true; | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public boolean canLeadOutOfScope(IInputKey key) { | ||
120 | ensureValidKey(key); | ||
121 | if (key instanceof EStructuralFeatureInstancesKey) { | ||
122 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
123 | if (feature instanceof EReference){ | ||
124 | return canLeadOutOfScope((EReference) feature); | ||
125 | } | ||
126 | } | ||
127 | return false; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Tells whether the given reference may lead out of scope. | ||
132 | * @since 2.1 | ||
133 | */ | ||
134 | public boolean canLeadOutOfScope(EReference reference) { | ||
135 | // Is it possible that this edge is dangling, i.e. its target lies outside of the scope? | ||
136 | // Unless non-dangling is globally assumed, | ||
137 | // proxy-resolving references (incl. containment) might point to proxies and are thus considered unsafe. | ||
138 | // Additionally, if the scope is sub-resource (containment subtree of object), | ||
139 | // all non-containment edges are also unsafe. | ||
140 | // Note that in case of cross-resource containment, | ||
141 | // the scope includes the contained object even if it is in a foreign resource. | ||
142 | return (!assumeNonDangling) | ||
143 | && (reference.isResolveProxies() || (subResourceScopeSplit && !reference.isContainment())); | ||
144 | } | ||
145 | |||
146 | @Override | ||
147 | public boolean isStateless(IInputKey key) { | ||
148 | ensureValidKey(key); | ||
149 | return key instanceof JavaTransitiveInstancesKey || key instanceof EClassUnscopedTransitiveInstancesKey; | ||
150 | } | ||
151 | |||
152 | @Override | ||
153 | public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { | ||
154 | ensureValidKey(key); | ||
155 | if (key instanceof EStructuralFeatureInstancesKey) { | ||
156 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
157 | final Map<Set<Integer>, Set<Integer>> result = | ||
158 | new HashMap<Set<Integer>, Set<Integer>>(); | ||
159 | if (isFeatureMultiplicityToOne(feature)) | ||
160 | result.put(Collections.singleton(0), Collections.singleton(1)); | ||
161 | if (isFeatureMultiplicityOneTo(feature)) | ||
162 | result.put(Collections.singleton(1), Collections.singleton(0)); | ||
163 | return result; | ||
164 | } else { | ||
165 | return Collections.emptyMap(); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * @since 2.1 | ||
171 | */ | ||
172 | public EClassTransitiveInstancesKey getSourceTypeKey(EStructuralFeatureInstancesKey key) { | ||
173 | return new EClassTransitiveInstancesKey(key.getEmfKey().getEContainingClass()); | ||
174 | } | ||
175 | /** | ||
176 | * @since 2.1 | ||
177 | */ | ||
178 | public IInputKey getTargetTypeKey(EStructuralFeatureInstancesKey key) { | ||
179 | EStructuralFeature feature = key.getEmfKey(); | ||
180 | if (feature instanceof EAttribute) { | ||
181 | return new EDataTypeInSlotsKey(((EAttribute) feature).getEAttributeType()); | ||
182 | } else if (feature instanceof EReference) { | ||
183 | EClass eReferenceType = ((EReference) feature).getEReferenceType(); | ||
184 | if (canLeadOutOfScope(key)) { | ||
185 | return new EClassUnscopedTransitiveInstancesKey(eReferenceType); | ||
186 | } else { | ||
187 | return new EClassTransitiveInstancesKey(eReferenceType); | ||
188 | } | ||
189 | } else throw new IllegalArgumentException(); | ||
190 | } | ||
191 | |||
192 | @Override | ||
193 | public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { | ||
194 | ensureValidKey(implyingKey); | ||
195 | Collection<InputKeyImplication> result = new HashSet<InputKeyImplication>(); | ||
196 | |||
197 | if (implyingKey instanceof EClassTransitiveInstancesKey) { | ||
198 | EClass eClass = ((EClassTransitiveInstancesKey) implyingKey).getEmfKey(); | ||
199 | |||
200 | // direct eSuperClasses | ||
201 | EList<EClass> directSuperTypes = eClass.getESuperTypes(); | ||
202 | if (!directSuperTypes.isEmpty()) { | ||
203 | for (EClass superType : directSuperTypes) { | ||
204 | final EClassTransitiveInstancesKey implied = new EClassTransitiveInstancesKey(superType); | ||
205 | result.add(new InputKeyImplication(implyingKey, implied, Arrays.asList(0))); | ||
206 | } | ||
207 | } else { | ||
208 | if (!EOBJECT_SCOPED_KEY.equals(implyingKey)) { | ||
209 | result.add(new InputKeyImplication(implyingKey, EOBJECT_SCOPED_KEY, Arrays.asList(0))); | ||
210 | } | ||
211 | } | ||
212 | // implies unscoped | ||
213 | if (UnscopedTypeSupport.EMIT_NEVER != emitUnscopedEClassTypes) | ||
214 | result.add(new InputKeyImplication(implyingKey, | ||
215 | new EClassUnscopedTransitiveInstancesKey(eClass), | ||
216 | Arrays.asList(0))); | ||
217 | } else if (implyingKey instanceof EClassUnscopedTransitiveInstancesKey) { | ||
218 | EClass eClass = ((EClassUnscopedTransitiveInstancesKey) implyingKey).getEmfKey(); | ||
219 | |||
220 | // direct eSuperClasses | ||
221 | EList<EClass> directSuperTypes = eClass.getESuperTypes(); | ||
222 | if (!directSuperTypes.isEmpty()) { | ||
223 | for (EClass superType : directSuperTypes) { | ||
224 | final EClassUnscopedTransitiveInstancesKey implied = new EClassUnscopedTransitiveInstancesKey( | ||
225 | superType); | ||
226 | result.add(new InputKeyImplication(implyingKey, implied, Arrays.asList(0))); | ||
227 | } | ||
228 | } else { | ||
229 | if (!EOBJECT_UNSCOPED_KEY.equals(implyingKey)) { | ||
230 | result.add(new InputKeyImplication(implyingKey, EOBJECT_UNSCOPED_KEY, Arrays.asList(0))); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | } else if (implyingKey instanceof JavaTransitiveInstancesKey) { | ||
235 | Class<?> instanceClass = ((JavaTransitiveInstancesKey) implyingKey).getInstanceClass(); | ||
236 | if (instanceClass != null) { // resolution successful | ||
237 | // direct Java superClass | ||
238 | Class<?> superclass = instanceClass.getSuperclass(); | ||
239 | if (superclass != null) { | ||
240 | JavaTransitiveInstancesKey impliedSuper = new JavaTransitiveInstancesKey(superclass); | ||
241 | result.add(new InputKeyImplication(implyingKey, impliedSuper, Arrays.asList(0))); | ||
242 | } | ||
243 | |||
244 | // direct Java superInterfaces | ||
245 | for (Class<?> superInterface : instanceClass.getInterfaces()) { | ||
246 | if (superInterface != null) { | ||
247 | JavaTransitiveInstancesKey impliedInterface = new JavaTransitiveInstancesKey(superInterface); | ||
248 | result.add(new InputKeyImplication(implyingKey, impliedInterface, Arrays.asList(0))); | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | } else if (implyingKey instanceof EStructuralFeatureInstancesKey) { | ||
254 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) implyingKey).getEmfKey(); | ||
255 | |||
256 | // source and target type | ||
257 | final EClass sourceType = featureSourceType(feature); | ||
258 | final EClassTransitiveInstancesKey impliedSource = new EClassTransitiveInstancesKey(sourceType); | ||
259 | final EClassifier targetType = featureTargetType(feature); | ||
260 | final IInputKey impliedTarget; | ||
261 | if (feature instanceof EReference) { | ||
262 | EReference reference = (EReference) feature; | ||
263 | |||
264 | if (!canLeadOutOfScope(reference)) { | ||
265 | impliedTarget = new EClassTransitiveInstancesKey((EClass) targetType); | ||
266 | } else { | ||
267 | impliedTarget = (UnscopedTypeSupport.EMIT_NEVER != emitUnscopedEClassTypes) ? | ||
268 | new EClassUnscopedTransitiveInstancesKey((EClass) targetType) | ||
269 | : null; | ||
270 | } | ||
271 | } else { // EDatatype | ||
272 | impliedTarget = new EDataTypeInSlotsKey((EDataType) targetType); | ||
273 | } | ||
274 | |||
275 | result.add(new InputKeyImplication(implyingKey, impliedSource, Arrays.asList(0))); | ||
276 | if (impliedTarget != null) | ||
277 | result.add(new InputKeyImplication(implyingKey, impliedTarget, Arrays.asList(1))); | ||
278 | |||
279 | // opposite | ||
280 | EReference opposite = featureOpposite(feature); | ||
281 | if (opposite != null && !canLeadOutOfScope((EReference) feature)) { | ||
282 | EStructuralFeatureInstancesKey impliedOpposite = new EStructuralFeatureInstancesKey(opposite); | ||
283 | result.add(new InputKeyImplication(implyingKey, impliedOpposite, Arrays.asList(1, 0))); | ||
284 | } | ||
285 | |||
286 | // containment | ||
287 | // TODO | ||
288 | } else if (implyingKey instanceof EDataTypeInSlotsKey) { | ||
289 | EDataType dataType = ((EDataTypeInSlotsKey) implyingKey).getEmfKey(); | ||
290 | |||
291 | // instance class of datatype | ||
292 | // TODO this can have a generation gap! (could be some dynamic EMF impl or whatever) | ||
293 | Class<?> instanceClass = dataType.getInstanceClass(); | ||
294 | if (instanceClass != null) { | ||
295 | JavaTransitiveInstancesKey implied = new JavaTransitiveInstancesKey(instanceClass); | ||
296 | result.add(new InputKeyImplication(implyingKey, implied, Arrays.asList(0))); | ||
297 | } | ||
298 | } else { | ||
299 | illegalInputKey(implyingKey); | ||
300 | } | ||
301 | |||
302 | return result; | ||
303 | } | ||
304 | |||
305 | @Override | ||
306 | public Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey) { | ||
307 | ensureValidKey(implyingKey); | ||
308 | if (implyingKey instanceof EClassUnscopedTransitiveInstancesKey) { | ||
309 | EClass emfKey = ((EClassUnscopedTransitiveInstancesKey) implyingKey).getEmfKey(); | ||
310 | |||
311 | Map<InputKeyImplication, Set<InputKeyImplication>> result = new HashMap<>(); | ||
312 | result.put( | ||
313 | new InputKeyImplication(implyingKey, EOBJECT_SCOPED_KEY, Arrays.asList(0)), | ||
314 | new HashSet<>(Arrays.asList(new InputKeyImplication(implyingKey, new EClassTransitiveInstancesKey(emfKey), Arrays.asList(0)))) | ||
315 | ); | ||
316 | return result; | ||
317 | } else return super.getConditionalImplications(implyingKey); | ||
318 | } | ||
319 | |||
320 | @Override | ||
321 | public Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey) { | ||
322 | ensureValidKey(implyingKey); | ||
323 | if (UnscopedTypeSupport.EMIT_ALWAYS == emitUnscopedEClassTypes && implyingKey instanceof EClassTransitiveInstancesKey) { | ||
324 | EClass emfKey = ((EClassTransitiveInstancesKey) implyingKey).getEmfKey(); | ||
325 | |||
326 | Collection<InputKeyImplication> result = new HashSet<InputKeyImplication>(); | ||
327 | result.add( | ||
328 | // in some cases, filtering by the the unscoped key may be sufficient | ||
329 | new InputKeyImplication(implyingKey, new EClassUnscopedTransitiveInstancesKey(emfKey), Arrays.asList(0)) | ||
330 | ); | ||
331 | return result; | ||
332 | } else return super.getWeakenedAlternatives(implyingKey); | ||
333 | } | ||
334 | |||
335 | public void ensureValidKey(IInputKey key) { | ||
336 | if (! (key instanceof BaseEMFTypeKey<?>) && ! (key instanceof JavaTransitiveInstancesKey)) | ||
337 | illegalInputKey(key); | ||
338 | } | ||
339 | |||
340 | public void illegalInputKey(IInputKey key) { | ||
341 | throw new IllegalArgumentException("The input key " + key + " is not a valid EMF input key."); | ||
342 | } | ||
343 | |||
344 | public boolean isFeatureMultiplicityToOne(EStructuralFeature feature) { | ||
345 | return !feature.isMany(); | ||
346 | } | ||
347 | |||
348 | public boolean isFeatureMultiplicityOneTo(EStructuralFeature typeObject) { | ||
349 | if (typeObject instanceof EReference) { | ||
350 | final EReference feature = (EReference)typeObject; | ||
351 | final EReference eOpposite = feature.getEOpposite(); | ||
352 | return feature.isContainment() || (eOpposite != null && !eOpposite.isMany()); | ||
353 | } else return false; | ||
354 | } | ||
355 | |||
356 | public EClass featureSourceType(EStructuralFeature feature) { | ||
357 | return feature.getEContainingClass(); | ||
358 | } | ||
359 | public EClassifier featureTargetType(EStructuralFeature typeObject) { | ||
360 | if (typeObject instanceof EAttribute) { | ||
361 | EAttribute attribute = (EAttribute) typeObject; | ||
362 | return attribute.getEAttributeType(); | ||
363 | } else if (typeObject instanceof EReference) { | ||
364 | EReference reference = (EReference) typeObject; | ||
365 | return reference.getEReferenceType(); | ||
366 | } else | ||
367 | throw new IllegalArgumentException("typeObject has invalid type " + typeObject.getClass().getName()); | ||
368 | } | ||
369 | public EReference featureOpposite(EStructuralFeature typeObject) { | ||
370 | if (typeObject instanceof EReference) { | ||
371 | EReference reference = (EReference) typeObject; | ||
372 | return reference.getEOpposite(); | ||
373 | } else return null; | ||
374 | } | ||
375 | |||
376 | @Override | ||
377 | public Comparator<IInputKey> getSuggestedEliminationOrdering() { | ||
378 | return SUGGESTED_ELIMINATION_ORDERING; | ||
379 | } | ||
380 | |||
381 | private static final Comparator<IInputKey> SUGGESTED_ELIMINATION_ORDERING = new Comparator<IInputKey>() { | ||
382 | @Override | ||
383 | public int compare(IInputKey o1, IInputKey o2) { | ||
384 | if (o1 instanceof EClassTransitiveInstancesKey && o2 instanceof EClassTransitiveInstancesKey) { | ||
385 | // common EClass types with many instances should be eliminated before rare types | ||
386 | return getRarity((EClassTransitiveInstancesKey)o1) - getRarity((EClassTransitiveInstancesKey)o2); | ||
387 | } else { | ||
388 | return getKeyTypeEliminationSequence(o1) - getKeyTypeEliminationSequence(o2); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | // The more supertypes there are, the more specialized the type | ||
393 | // the more specialized the type, the rarer instances are expected to be found | ||
394 | private int getRarity(EClassTransitiveInstancesKey key) { | ||
395 | return key.getEmfKey().getEAllSuperTypes().size() + (EOBJECT_SCOPED_KEY.equals(key) ? 0 : 1); | ||
396 | } | ||
397 | |||
398 | // Scoped EClass transitive instance keys are attempted to be eliminated before all else | ||
399 | // so that e.g. their unscoped version can eliminate them is variable is known to be scoped | ||
400 | private int getKeyTypeEliminationSequence(IInputKey o1) { | ||
401 | return (o1 instanceof EClassTransitiveInstancesKey) ? -1 : 0; | ||
402 | } | ||
403 | }; | ||
404 | |||
405 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryRuntimeContext.java new file mode 100644 index 00000000..7809cd24 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryRuntimeContext.java | |||
@@ -0,0 +1,839 @@ | |||
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.emf; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.EnumSet; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.Map; | ||
18 | import java.util.Optional; | ||
19 | import java.util.Set; | ||
20 | import java.util.concurrent.Callable; | ||
21 | import java.util.function.Function; | ||
22 | import java.util.stream.Collectors; | ||
23 | |||
24 | import org.apache.log4j.Logger; | ||
25 | import org.eclipse.emf.ecore.EClass; | ||
26 | import org.eclipse.emf.ecore.EDataType; | ||
27 | import org.eclipse.emf.ecore.EObject; | ||
28 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
29 | import tools.refinery.viatra.runtime.base.api.DataTypeListener; | ||
30 | import tools.refinery.viatra.runtime.base.api.FeatureListener; | ||
31 | import tools.refinery.viatra.runtime.base.api.IndexingLevel; | ||
32 | import tools.refinery.viatra.runtime.base.api.InstanceListener; | ||
33 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
34 | import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey; | ||
35 | import tools.refinery.viatra.runtime.emf.types.EClassUnscopedTransitiveInstancesKey; | ||
36 | import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey; | ||
37 | import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey; | ||
38 | import tools.refinery.viatra.runtime.matchers.context.AbstractQueryRuntimeContext; | ||
39 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
40 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
41 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
42 | import tools.refinery.viatra.runtime.matchers.context.IndexingService; | ||
43 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
44 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
45 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
46 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
47 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
48 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
49 | |||
50 | /** | ||
51 | * The EMF-based runtime query context, backed by an IQBase NavigationHelper. | ||
52 | * | ||
53 | * @author Bergmann Gabor | ||
54 | * | ||
55 | * <p> TODO: {@link #ensureIndexed(EClass)} may be inefficient if supertype already cached. | ||
56 | * @since 1.4 | ||
57 | */ | ||
58 | public class EMFQueryRuntimeContext extends AbstractQueryRuntimeContext { | ||
59 | protected final NavigationHelper baseIndex; | ||
60 | //private BaseIndexListener listener; | ||
61 | |||
62 | protected final Map<EClass, EnumSet<IndexingService>> indexedClasses = new HashMap<>(); | ||
63 | protected final Map<EDataType, EnumSet<IndexingService>> indexedDataTypes = new HashMap<>(); | ||
64 | protected final Map<EStructuralFeature, EnumSet<IndexingService>> indexedFeatures = new HashMap<>(); | ||
65 | |||
66 | protected final EMFQueryMetaContext metaContext; | ||
67 | |||
68 | protected Logger logger; | ||
69 | |||
70 | private EMFScope emfScope; | ||
71 | |||
72 | public EMFQueryRuntimeContext(NavigationHelper baseIndex, Logger logger, EMFScope emfScope) { | ||
73 | this.baseIndex = baseIndex; | ||
74 | this.logger = logger; | ||
75 | this.metaContext = new EMFQueryMetaContext(emfScope); | ||
76 | this.emfScope = emfScope; | ||
77 | } | ||
78 | |||
79 | public EMFScope getEmfScope() { | ||
80 | return emfScope; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Utility method to add an indexing service to a given key. Returns true if the requested service was | ||
85 | * not present before this call. | ||
86 | * @param map | ||
87 | * @param key | ||
88 | * @param service | ||
89 | * @return | ||
90 | */ | ||
91 | private static <K> boolean addIndexingService(Map<K, EnumSet<IndexingService>> map, K key, IndexingService service){ | ||
92 | EnumSet<IndexingService> current = map.get(key); | ||
93 | if (current == null){ | ||
94 | current = EnumSet.of(service); | ||
95 | map.put(key, current); | ||
96 | return true; | ||
97 | }else{ | ||
98 | return current.add(service); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | public void dispose() { | ||
103 | //baseIndex.removeFeatureListener(indexedFeatures, listener); | ||
104 | indexedFeatures.clear(); | ||
105 | //baseIndex.removeInstanceListener(indexedClasses, listener); | ||
106 | indexedClasses.clear(); | ||
107 | //baseIndex.removeDataTypeListener(indexedDataTypes, listener); | ||
108 | indexedDataTypes.clear(); | ||
109 | |||
110 | // No need to remove listeners, as NavHelper will be disposed imminently. | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
115 | return baseIndex.coalesceTraversals(callable); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public boolean isCoalescing() { | ||
120 | return baseIndex.isCoalescing(); | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public IQueryMetaContext getMetaContext() { | ||
125 | return metaContext; | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public void ensureIndexed(IInputKey key, IndexingService service) { | ||
130 | ensureEnumerableKey(key); | ||
131 | if (key instanceof EClassTransitiveInstancesKey) { | ||
132 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
133 | ensureIndexed(eClass, service); | ||
134 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
135 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
136 | ensureIndexed(dataType, service); | ||
137 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
138 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
139 | ensureIndexed(feature, service); | ||
140 | } else { | ||
141 | illegalInputKey(key); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Retrieve the current registered indexing services for the given key. May not return null, | ||
147 | * returns an empty set if no indexing is registered. | ||
148 | * | ||
149 | * @since 1.4 | ||
150 | */ | ||
151 | protected EnumSet<IndexingService> getCurrentIndexingServiceFor(IInputKey key){ | ||
152 | ensureEnumerableKey(key); | ||
153 | if (key instanceof EClassTransitiveInstancesKey) { | ||
154 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
155 | EnumSet<IndexingService> is = indexedClasses.get(eClass); | ||
156 | return is == null ? EnumSet.noneOf(IndexingService.class) : is; | ||
157 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
158 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
159 | EnumSet<IndexingService> is = indexedDataTypes.get(dataType); | ||
160 | return is == null ? EnumSet.noneOf(IndexingService.class) : is; | ||
161 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
162 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
163 | EnumSet<IndexingService> is = indexedFeatures.get(feature); | ||
164 | return is == null ? EnumSet.noneOf(IndexingService.class) : is; | ||
165 | } else { | ||
166 | illegalInputKey(key); | ||
167 | return EnumSet.noneOf(IndexingService.class); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | @Override | ||
172 | public boolean isIndexed(IInputKey key, IndexingService service) { | ||
173 | return getCurrentIndexingServiceFor(key).contains(service); | ||
174 | } | ||
175 | |||
176 | @Override | ||
177 | public boolean containsTuple(IInputKey key, ITuple seed) { | ||
178 | ensureValidKey(key); | ||
179 | if (key instanceof JavaTransitiveInstancesKey) { | ||
180 | Class<?> instanceClass = forceGetWrapperInstanceClass((JavaTransitiveInstancesKey) key); | ||
181 | return instanceClass != null && instanceClass.isInstance(seed.get(0)); | ||
182 | } else if (key instanceof EClassUnscopedTransitiveInstancesKey) { | ||
183 | EClass emfKey = ((EClassUnscopedTransitiveInstancesKey) key).getEmfKey(); | ||
184 | Object candidateInstance = seed.get(0); | ||
185 | return candidateInstance instanceof EObject | ||
186 | && baseIndex.isInstanceOfUnscoped((EObject) candidateInstance, emfKey); | ||
187 | } else { | ||
188 | ensureIndexed(key, IndexingService.INSTANCES); | ||
189 | if (key instanceof EClassTransitiveInstancesKey) { | ||
190 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
191 | // instance check not enough to satisfy scoping, must lookup from index | ||
192 | Object candidateInstance = seed.get(0); | ||
193 | return candidateInstance instanceof EObject | ||
194 | && baseIndex.isInstanceOfScoped((EObject) candidateInstance, eClass); | ||
195 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
196 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
197 | return baseIndex.isInstanceOfDatatype(seed.get(0), dataType); | ||
198 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
199 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
200 | Object sourceCandidate = seed.get(0); | ||
201 | return sourceCandidate instanceof EObject | ||
202 | && baseIndex.isFeatureInstance((EObject) sourceCandidate, seed.get(1), feature); | ||
203 | } else { | ||
204 | illegalInputKey(key); | ||
205 | return false; | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | |||
210 | private Class<?> forceGetWrapperInstanceClass(JavaTransitiveInstancesKey key) { | ||
211 | Class<?> instanceClass; | ||
212 | try { | ||
213 | instanceClass = key.forceGetWrapperInstanceClass(); | ||
214 | } catch (ClassNotFoundException e) { | ||
215 | logger.error("Could not load instance class for type constraint " + key.getWrappedKey(), e); | ||
216 | instanceClass = null; | ||
217 | } | ||
218 | return instanceClass; | ||
219 | } | ||
220 | |||
221 | @Override | ||
222 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
223 | ensureIndexed(key, IndexingService.INSTANCES); | ||
224 | final Collection<Tuple> result = new HashSet<Tuple>(); | ||
225 | |||
226 | if (key instanceof EClassTransitiveInstancesKey) { | ||
227 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
228 | |||
229 | if (seedMask.indices.length == 0) { // unseeded | ||
230 | return baseIndex.getAllInstances(eClass).stream().map(wrapUnary).collect(Collectors.toSet()); | ||
231 | } else { // fully seeded | ||
232 | Object seedInstance = seedMask.getValue(seed, 0); | ||
233 | if (containsTuple(key, seed)) | ||
234 | result.add(Tuples.staticArityFlatTupleOf(seedInstance)); | ||
235 | } | ||
236 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
237 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
238 | |||
239 | if (seedMask.indices.length == 0) { // unseeded | ||
240 | return baseIndex.getDataTypeInstances(dataType).stream().map(wrapUnary).collect(Collectors.toSet()); | ||
241 | } else { // fully seeded | ||
242 | Object seedInstance = seedMask.getValue(seed, 0); | ||
243 | if (containsTuple(key, seed)) | ||
244 | result.add(Tuples.staticArityFlatTupleOf(seedInstance)); | ||
245 | } | ||
246 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
247 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
248 | |||
249 | boolean isSourceBound = false; | ||
250 | int sourceIndex = -1; | ||
251 | boolean isTargetBound = false; | ||
252 | int targetIndex = -1; | ||
253 | for (int i = 0; i < seedMask.getSize(); i++) { | ||
254 | int index = seedMask.indices[i]; | ||
255 | if (index == 0) { | ||
256 | isSourceBound = true; | ||
257 | sourceIndex = i; | ||
258 | } else if (index == 1) { | ||
259 | isTargetBound = true; | ||
260 | targetIndex = i; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | if (!isSourceBound && isTargetBound) { | ||
265 | final Object seedTarget = seed.get(targetIndex); | ||
266 | final Set<EObject> results = baseIndex.findByFeatureValue(seedTarget, feature); | ||
267 | return results.stream().map(obj -> Tuples.staticArityFlatTupleOf(obj, seedTarget)).collect(Collectors.toSet()); | ||
268 | } else if (isSourceBound && isTargetBound) { // fully seeded | ||
269 | final Object seedSource = seed.get(sourceIndex); | ||
270 | final Object seedTarget = seed.get(targetIndex); | ||
271 | if (containsTuple(key, seed)) | ||
272 | result.add(Tuples.staticArityFlatTupleOf(seedSource, seedTarget)); | ||
273 | } else if (!isSourceBound && !isTargetBound) { // fully unseeded | ||
274 | baseIndex.processAllFeatureInstances(feature, (source, target) -> result.add(Tuples.staticArityFlatTupleOf(source, target))); | ||
275 | } else if (isSourceBound && !isTargetBound) { | ||
276 | final Object seedSource = seed.get(sourceIndex); | ||
277 | final Set<Object> results = baseIndex.getFeatureTargets((EObject) seedSource, feature); | ||
278 | return results.stream().map(obj -> Tuples.staticArityFlatTupleOf(seedSource, obj)).collect(Collectors.toSet()); | ||
279 | } | ||
280 | } else { | ||
281 | illegalInputKey(key); | ||
282 | } | ||
283 | |||
284 | |||
285 | return result; | ||
286 | } | ||
287 | |||
288 | private static Function<Object, Tuple> wrapUnary = Tuples::staticArityFlatTupleOf; | ||
289 | |||
290 | @Override | ||
291 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
292 | ensureIndexed(key, IndexingService.INSTANCES); | ||
293 | |||
294 | if (key instanceof EClassTransitiveInstancesKey) { | ||
295 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
296 | |||
297 | if (seedMask.indices.length == 0) { // unseeded | ||
298 | return baseIndex.getAllInstances(eClass); | ||
299 | } else { | ||
300 | // must be unseeded, this is enumerateValues after all! | ||
301 | illegalEnumerateValues(seed.toImmutable()); | ||
302 | } | ||
303 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
304 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
305 | |||
306 | if (seedMask.indices.length == 0) { // unseeded | ||
307 | return baseIndex.getDataTypeInstances(dataType); | ||
308 | } else { | ||
309 | // must be unseeded, this is enumerateValues after all! | ||
310 | illegalEnumerateValues(seed.toImmutable()); | ||
311 | } | ||
312 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
313 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
314 | |||
315 | boolean isSourceBound = false; | ||
316 | int sourceIndex = -1; | ||
317 | boolean isTargetBound = false; | ||
318 | int targetIndex = -1; | ||
319 | for (int i = 0; i < seedMask.getSize(); i++) { | ||
320 | int index = seedMask.indices[i]; | ||
321 | if (index == 0) { | ||
322 | isSourceBound = true; | ||
323 | sourceIndex = i; | ||
324 | } else if (index == 1) { | ||
325 | isTargetBound = true; | ||
326 | targetIndex = i; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | if (!isSourceBound && isTargetBound) { | ||
331 | Object seedTarget = seed.get(targetIndex); | ||
332 | return baseIndex.findByFeatureValue(seedTarget, feature); | ||
333 | } else if (isSourceBound && !isTargetBound) { | ||
334 | Object seedSource = seed.get(sourceIndex); | ||
335 | return baseIndex.getFeatureTargets((EObject) seedSource, feature); | ||
336 | } else { | ||
337 | // must be singly unseeded, this is enumerateValues after all! | ||
338 | illegalEnumerateValues(seed.toImmutable()); | ||
339 | } | ||
340 | } else { | ||
341 | illegalInputKey(key); | ||
342 | } | ||
343 | return null; | ||
344 | } | ||
345 | |||
346 | @Override | ||
347 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { | ||
348 | ensureIndexed(key, IndexingService.STATISTICS); | ||
349 | |||
350 | if (key instanceof EClassTransitiveInstancesKey) { | ||
351 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
352 | |||
353 | if (seedMask.indices.length == 0) { // unseeded | ||
354 | return baseIndex.countAllInstances(eClass); | ||
355 | } else { // fully seeded | ||
356 | return (containsTuple(key, seed)) ? 1 : 0; | ||
357 | } | ||
358 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
359 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
360 | |||
361 | if (seedMask.indices.length == 0) { // unseeded | ||
362 | return baseIndex.countDataTypeInstances(dataType); | ||
363 | } else { // fully seeded | ||
364 | return (containsTuple(key, seed)) ? 1 : 0; | ||
365 | } | ||
366 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
367 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
368 | |||
369 | boolean isSourceBound = false; | ||
370 | int sourceIndex = -1; | ||
371 | boolean isTargetBound = false; | ||
372 | int targetIndex = -1; | ||
373 | for (int i = 0; i < seedMask.getSize(); i++) { | ||
374 | int index = seedMask.indices[i]; | ||
375 | if (index == 0) { | ||
376 | isSourceBound = true; | ||
377 | sourceIndex = i; | ||
378 | } else if (index == 1) { | ||
379 | isTargetBound = true; | ||
380 | targetIndex = i; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | if (!isSourceBound && isTargetBound) { | ||
385 | final Object seedTarget = seed.get(targetIndex); | ||
386 | return baseIndex.findByFeatureValue(seedTarget, feature).size(); | ||
387 | } else if (isSourceBound && isTargetBound) { // fully seeded | ||
388 | return (containsTuple(key, seed)) ? 1 : 0; | ||
389 | } else if (!isSourceBound && !isTargetBound) { // fully unseeded | ||
390 | return baseIndex.countFeatures(feature); | ||
391 | } else if (isSourceBound && !isTargetBound) { | ||
392 | final Object seedSource = seed.get(sourceIndex); | ||
393 | return baseIndex.countFeatureTargets((EObject) seedSource, feature); | ||
394 | } | ||
395 | } else { | ||
396 | illegalInputKey(key); | ||
397 | } | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | |||
402 | /** | ||
403 | * @since 2.1 | ||
404 | */ | ||
405 | @Override | ||
406 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
407 | |||
408 | if (key instanceof EClassTransitiveInstancesKey) { | ||
409 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
410 | |||
411 | if (isIndexed(key, IndexingService.STATISTICS)) { // exact answer known | ||
412 | if (groupMask.indices.length == 0) { // empty projection | ||
413 | return (0 != baseIndex.countAllInstances(eClass)) ? Optional.of(1L) : Optional.of(0L); | ||
414 | } else { // unprojected | ||
415 | return Optional.of((long)baseIndex.countAllInstances(eClass)); | ||
416 | } | ||
417 | } else return Optional.empty(); // TODO use known supertype counts as upper, subtypes as lower bounds | ||
418 | |||
419 | } else if (key instanceof EClassUnscopedTransitiveInstancesKey) { | ||
420 | EClass eClass = ((EClassUnscopedTransitiveInstancesKey) key).getEmfKey(); | ||
421 | |||
422 | // can give only lower bound based on the scoped key | ||
423 | if (Accuracy.BEST_LOWER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { | ||
424 | return estimateCardinality(new EClassTransitiveInstancesKey(eClass), groupMask, requiredAccuracy); | ||
425 | } else return Optional.empty(); | ||
426 | |||
427 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
428 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
429 | |||
430 | if (isIndexed(key, IndexingService.STATISTICS)) { | ||
431 | if (groupMask.indices.length == 0) { // empty projection | ||
432 | return (0 != baseIndex.countDataTypeInstances(dataType)) ? Optional.of(1L) : Optional.of(0L); | ||
433 | } else { // unprojected | ||
434 | return Optional.of((long)baseIndex.countDataTypeInstances(dataType)); | ||
435 | } | ||
436 | } else return Optional.empty(); | ||
437 | |||
438 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
439 | EStructuralFeatureInstancesKey featureKey = (EStructuralFeatureInstancesKey) key; | ||
440 | EStructuralFeature feature = featureKey.getEmfKey(); | ||
441 | |||
442 | |||
443 | boolean isSourceSelected = false; | ||
444 | boolean isTargetSelected = false; | ||
445 | for (int i = 0; i < groupMask.getSize(); i++) { | ||
446 | int index = groupMask.indices[i]; | ||
447 | if (index == 0) { | ||
448 | isSourceSelected = true; | ||
449 | } else if (index == 1) { | ||
450 | isTargetSelected = true; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | Optional<Long> sourceTypeUpperEstimate = | ||
455 | estimateCardinality(metaContext.getSourceTypeKey(featureKey), | ||
456 | TupleMask.identity(1), Accuracy.BEST_UPPER_BOUND); | ||
457 | Optional<Long> targetTypeUpperEstimate = | ||
458 | estimateCardinality(metaContext.getTargetTypeKey(featureKey), | ||
459 | TupleMask.identity(1), Accuracy.BEST_UPPER_BOUND); | ||
460 | |||
461 | if (!isSourceSelected && !isTargetSelected) { // empty projection | ||
462 | if (isIndexed(key, IndexingService.STATISTICS)) { // we have exact node counts | ||
463 | return (0 == baseIndex.countFeatures(feature)) ? Optional.of(0L) : Optional.of(1L); | ||
464 | } else { // we can still say 0 in a few cases | ||
465 | if (0 == sourceTypeUpperEstimate.orElse(-1L)) | ||
466 | return Optional.of(0L); | ||
467 | |||
468 | if (0 == targetTypeUpperEstimate.orElse(-1L)) | ||
469 | return Optional.of(0L); | ||
470 | |||
471 | return Optional.empty(); | ||
472 | } | ||
473 | |||
474 | } else if (isSourceSelected && !isTargetSelected) { // count sources | ||
475 | if (isIndexed(key, IndexingService.INSTANCES)) { // we have instances, therefore feature end counts | ||
476 | return Optional.of((long)(baseIndex.getHoldersOfFeature(feature).size())); | ||
477 | } else if (metaContext.isFeatureMultiplicityToOne(feature) && | ||
478 | isIndexed(key, IndexingService.STATISTICS)) { // count of edges = count of sources due to func. dep. | ||
479 | return Optional.of((long)(baseIndex.countFeatures(feature))); | ||
480 | } else if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { | ||
481 | // upper bound by source type | ||
482 | Optional<Long> estimate = sourceTypeUpperEstimate; | ||
483 | // total edge counts are another upper bound (even if instances are unindexed) | ||
484 | if (isIndexed(key, IndexingService.STATISTICS)) { | ||
485 | estimate = Optional.of(Math.min( | ||
486 | baseIndex.countFeatures(feature), | ||
487 | estimate.orElse(Long.MAX_VALUE))); | ||
488 | } | ||
489 | return estimate; | ||
490 | } else return Optional.empty(); | ||
491 | |||
492 | } else if (!isSourceSelected /*&& isTargetSelected*/) { // count targets | ||
493 | if (isIndexed(key, IndexingService.INSTANCES)) { // we have instances, therefore feature end counts | ||
494 | return Optional.of((long)(baseIndex.getValuesOfFeature(feature).size())); | ||
495 | } else if (metaContext.isFeatureMultiplicityOneTo(feature) && | ||
496 | isIndexed(key, IndexingService.STATISTICS)) { // count of edges = count of targets due to func. dep. | ||
497 | return Optional.of((long)(baseIndex.countFeatures(feature))); | ||
498 | } else if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // upper bound by target type | ||
499 | // upper bound by target type | ||
500 | Optional<Long> estimate = targetTypeUpperEstimate; | ||
501 | // total edge counts are another upper bound (even if instances are unindexed) | ||
502 | if (isIndexed(key, IndexingService.STATISTICS)) { | ||
503 | estimate = Optional.of(Math.min( | ||
504 | baseIndex.countFeatures(feature), | ||
505 | estimate.orElse(Long.MAX_VALUE))); | ||
506 | } | ||
507 | return estimate; | ||
508 | } else return Optional.empty(); | ||
509 | |||
510 | } else { // (isSourceSelected && isTargetSelected) // count edges | ||
511 | if (isIndexed(key, IndexingService.STATISTICS)) { // we have exact edge counts | ||
512 | return Optional.of((long)baseIndex.countFeatures(feature)); | ||
513 | } else if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // overestimates may still be available | ||
514 | Optional<Long> estimate = // trivial upper bound: product of source & target type sizes (if available) | ||
515 | (sourceTypeUpperEstimate.isPresent() && targetTypeUpperEstimate.isPresent()) ? | ||
516 | Optional.of( | ||
517 | ((long)sourceTypeUpperEstimate.get()) * targetTypeUpperEstimate.get() | ||
518 | ) : Optional.empty(); | ||
519 | |||
520 | if (metaContext.isFeatureMultiplicityToOne(feature) && sourceTypeUpperEstimate.isPresent()) { | ||
521 | // upper bounded by source type due to func. dep. | ||
522 | estimate = Optional.of(Math.min( | ||
523 | sourceTypeUpperEstimate.get(), | ||
524 | estimate.orElse(Long.MAX_VALUE))); | ||
525 | } | ||
526 | if (metaContext.isFeatureMultiplicityOneTo(feature) && targetTypeUpperEstimate.isPresent()) { | ||
527 | // upper bounded by target type due to func. dep. | ||
528 | estimate = Optional.of(Math.min( | ||
529 | targetTypeUpperEstimate.get(), | ||
530 | estimate.orElse(Long.MAX_VALUE))); | ||
531 | } | ||
532 | |||
533 | return estimate; | ||
534 | } else return Optional.empty(); | ||
535 | } | ||
536 | |||
537 | } else { | ||
538 | return Optional.empty(); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | /** | ||
543 | * @since 2.1 | ||
544 | */ | ||
545 | @Override | ||
546 | public Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
547 | // smart handling of special cases | ||
548 | if (key instanceof EStructuralFeatureInstancesKey) { | ||
549 | EStructuralFeatureInstancesKey featureKey = (EStructuralFeatureInstancesKey) key; | ||
550 | EStructuralFeature feature = featureKey.getEmfKey(); | ||
551 | |||
552 | // special treatment for edge navigation | ||
553 | if (1 == groupMask.getSize()) { | ||
554 | if (0 == groupMask.indices[0] && metaContext.isFeatureMultiplicityToOne(feature)) { // count targets per source | ||
555 | return Optional.of(1.0); | ||
556 | } else if (1 == groupMask.indices[0] && metaContext.isFeatureMultiplicityOneTo(feature)) { // count sources per target | ||
557 | return Optional.of(1.0); | ||
558 | } | ||
559 | } | ||
560 | } | ||
561 | |||
562 | // keep the default behaviour | ||
563 | return super.estimateAverageBucketSize(key, groupMask, requiredAccuracy); | ||
564 | } | ||
565 | |||
566 | |||
567 | public void ensureEnumerableKey(IInputKey key) { | ||
568 | ensureValidKey(key); | ||
569 | if (! metaContext.isEnumerable(key)) | ||
570 | throw new IllegalArgumentException("Key is not enumerable: " + key); | ||
571 | |||
572 | } | ||
573 | |||
574 | public void ensureValidKey(IInputKey key) { | ||
575 | metaContext.ensureValidKey(key); | ||
576 | } | ||
577 | public void illegalInputKey(IInputKey key) { | ||
578 | metaContext.illegalInputKey(key); | ||
579 | } | ||
580 | public void illegalEnumerateValues(Tuple seed) { | ||
581 | throw new IllegalArgumentException("Must have exactly one unseeded element in enumerateValues() invocation, received instead: " + seed); | ||
582 | } | ||
583 | |||
584 | /** | ||
585 | * @since 1.4 | ||
586 | */ | ||
587 | public void ensureIndexed(EClass eClass, IndexingService service) { | ||
588 | if (addIndexingService(indexedClasses, eClass, service)) { | ||
589 | final Set<EClass> newClasses = Collections.singleton(eClass); | ||
590 | IndexingLevel level = IndexingLevel.toLevel(service); | ||
591 | if (!baseIndex.getIndexingLevel(eClass).providesLevel(level)) { | ||
592 | baseIndex.registerEClasses(newClasses, level); | ||
593 | } | ||
594 | //baseIndex.addInstanceListener(newClasses, listener); | ||
595 | } | ||
596 | } | ||
597 | |||
598 | /** | ||
599 | * @since 1.4 | ||
600 | */ | ||
601 | public void ensureIndexed(EDataType eDataType, IndexingService service) { | ||
602 | if (addIndexingService(indexedDataTypes, eDataType, service)) { | ||
603 | final Set<EDataType> newDataTypes = Collections.singleton(eDataType); | ||
604 | IndexingLevel level = IndexingLevel.toLevel(service); | ||
605 | if (!baseIndex.getIndexingLevel(eDataType).providesLevel(level)) { | ||
606 | baseIndex.registerEDataTypes(newDataTypes, level); | ||
607 | } | ||
608 | //baseIndex.addDataTypeListener(newDataTypes, listener); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | /** | ||
613 | * @since 1.4 | ||
614 | */ | ||
615 | public void ensureIndexed(EStructuralFeature feature, IndexingService service) { | ||
616 | if (addIndexingService(indexedFeatures, feature, service)) { | ||
617 | final Set<EStructuralFeature> newFeatures = Collections.singleton(feature); | ||
618 | IndexingLevel level = IndexingLevel.toLevel(service); | ||
619 | if (!baseIndex.getIndexingLevel(feature).providesLevel(level)) { | ||
620 | baseIndex.registerEStructuralFeatures(newFeatures, level); | ||
621 | } | ||
622 | //baseIndex.addFeatureListener(newFeatures, listener); | ||
623 | } | ||
624 | } | ||
625 | |||
626 | |||
627 | |||
628 | // UPDATE HANDLING SECTION | ||
629 | |||
630 | /** | ||
631 | * Abstract internal listener wrapper for a {@link IQueryRuntimeContextListener}. | ||
632 | * Due to the overridden equals/hashCode(), it is safe to create a new instance for the same listener. | ||
633 | * | ||
634 | * @author Bergmann Gabor | ||
635 | */ | ||
636 | private abstract static class ListenerAdapter { | ||
637 | IQueryRuntimeContextListener listener; | ||
638 | Tuple seed; | ||
639 | /** | ||
640 | * @param listener | ||
641 | * @param seed must be non-null | ||
642 | */ | ||
643 | public ListenerAdapter(IQueryRuntimeContextListener listener, Object... seed) { | ||
644 | this.listener = listener; | ||
645 | this.seed = Tuples.flatTupleOf(seed); | ||
646 | } | ||
647 | |||
648 | @Override | ||
649 | public int hashCode() { | ||
650 | final int prime = 31; | ||
651 | int result = 1; | ||
652 | result = prime * result | ||
653 | + ((listener == null) ? 0 : listener.hashCode()); | ||
654 | result = prime * result + ((seed == null) ? 0 : seed.hashCode()); | ||
655 | return result; | ||
656 | } | ||
657 | |||
658 | @Override | ||
659 | public boolean equals(Object obj) { | ||
660 | if (this == obj) | ||
661 | return true; | ||
662 | if (obj == null) | ||
663 | return false; | ||
664 | if (!(obj.getClass().equals(this.getClass()))) | ||
665 | return false; | ||
666 | ListenerAdapter other = (ListenerAdapter) obj; | ||
667 | if (listener == null) { | ||
668 | if (other.listener != null) | ||
669 | return false; | ||
670 | } else if (!listener.equals(other.listener)) | ||
671 | return false; | ||
672 | if (seed == null) { | ||
673 | if (other.seed != null) | ||
674 | return false; | ||
675 | } else if (!seed.equals(other.seed)) | ||
676 | return false; | ||
677 | return true; | ||
678 | } | ||
679 | |||
680 | |||
681 | @Override | ||
682 | public String toString() { | ||
683 | return "Wrapped<Seed:" + seed + ">#" + listener; | ||
684 | } | ||
685 | |||
686 | |||
687 | } | ||
688 | private static class EClassTransitiveInstancesAdapter extends ListenerAdapter implements InstanceListener { | ||
689 | private Object seedInstance; | ||
690 | public EClassTransitiveInstancesAdapter(IQueryRuntimeContextListener listener, Object seedInstance) { | ||
691 | super(listener, seedInstance); | ||
692 | this.seedInstance = seedInstance; | ||
693 | } | ||
694 | @Override | ||
695 | public void instanceInserted(EClass clazz, EObject instance) { | ||
696 | if (seedInstance != null && !seedInstance.equals(instance)) return; | ||
697 | listener.update(new EClassTransitiveInstancesKey(clazz), | ||
698 | Tuples.staticArityFlatTupleOf(instance), true); | ||
699 | } | ||
700 | @Override | ||
701 | public void instanceDeleted(EClass clazz, EObject instance) { | ||
702 | if (seedInstance != null && !seedInstance.equals(instance)) return; | ||
703 | listener.update(new EClassTransitiveInstancesKey(clazz), | ||
704 | Tuples.staticArityFlatTupleOf(instance), false); | ||
705 | } | ||
706 | } | ||
707 | private static class EDataTypeInSlotsAdapter extends ListenerAdapter implements DataTypeListener { | ||
708 | private Object seedValue; | ||
709 | public EDataTypeInSlotsAdapter(IQueryRuntimeContextListener listener, Object seedValue) { | ||
710 | super(listener, seedValue); | ||
711 | this.seedValue = seedValue; | ||
712 | } | ||
713 | @Override | ||
714 | public void dataTypeInstanceInserted(EDataType type, Object instance, | ||
715 | boolean firstOccurrence) { | ||
716 | if (firstOccurrence) { | ||
717 | if (seedValue != null && !seedValue.equals(instance)) return; | ||
718 | listener.update(new EDataTypeInSlotsKey(type), | ||
719 | Tuples.staticArityFlatTupleOf(instance), true); | ||
720 | } | ||
721 | } | ||
722 | @Override | ||
723 | public void dataTypeInstanceDeleted(EDataType type, Object instance, | ||
724 | boolean lastOccurrence) { | ||
725 | if (lastOccurrence) { | ||
726 | if (seedValue != null && !seedValue.equals(instance)) return; | ||
727 | listener.update(new EDataTypeInSlotsKey(type), | ||
728 | Tuples.staticArityFlatTupleOf(instance), false); | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | private static class EStructuralFeatureInstancesKeyAdapter extends ListenerAdapter implements FeatureListener { | ||
733 | private Object seedHost; | ||
734 | private Object seedValue; | ||
735 | public EStructuralFeatureInstancesKeyAdapter(IQueryRuntimeContextListener listener, Object seedHost, Object seedValue) { | ||
736 | super(listener, seedHost, seedValue); | ||
737 | this.seedHost = seedHost; | ||
738 | this.seedValue = seedValue; | ||
739 | } | ||
740 | @Override | ||
741 | public void featureInserted(EObject host, EStructuralFeature feature, | ||
742 | Object value) { | ||
743 | if (seedHost != null && !seedHost.equals(host)) return; | ||
744 | if (seedValue != null && !seedValue.equals(value)) return; | ||
745 | listener.update(new EStructuralFeatureInstancesKey(feature), | ||
746 | Tuples.staticArityFlatTupleOf(host, value), true); | ||
747 | } | ||
748 | @Override | ||
749 | public void featureDeleted(EObject host, EStructuralFeature feature, | ||
750 | Object value) { | ||
751 | if (seedHost != null && !seedHost.equals(host)) return; | ||
752 | if (seedValue != null && !seedValue.equals(value)) return; | ||
753 | listener.update(new EStructuralFeatureInstancesKey(feature), | ||
754 | Tuples.staticArityFlatTupleOf(host, value), false); | ||
755 | } | ||
756 | } | ||
757 | |||
758 | @Override | ||
759 | public void addUpdateListener(IInputKey key, Tuple seed /* TODO ignored */, IQueryRuntimeContextListener listener) { | ||
760 | // stateless, so NOP | ||
761 | if (key instanceof JavaTransitiveInstancesKey) return; | ||
762 | |||
763 | ensureIndexed(key, IndexingService.INSTANCES); | ||
764 | if (key instanceof EClassTransitiveInstancesKey) { | ||
765 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
766 | baseIndex.addInstanceListener(Collections.singleton(eClass), | ||
767 | new EClassTransitiveInstancesAdapter(listener, seed.get(0))); | ||
768 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
769 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
770 | baseIndex.addDataTypeListener(Collections.singleton(dataType), | ||
771 | new EDataTypeInSlotsAdapter(listener, seed.get(0))); | ||
772 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
773 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
774 | baseIndex.addFeatureListener(Collections.singleton(feature), | ||
775 | new EStructuralFeatureInstancesKeyAdapter(listener, seed.get(0), seed.get(1))); | ||
776 | } else { | ||
777 | illegalInputKey(key); | ||
778 | } | ||
779 | } | ||
780 | @Override | ||
781 | public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { | ||
782 | // stateless, so NOP | ||
783 | if (key instanceof JavaTransitiveInstancesKey) return; | ||
784 | |||
785 | ensureIndexed(key, IndexingService.INSTANCES); | ||
786 | if (key instanceof EClassTransitiveInstancesKey) { | ||
787 | EClass eClass = ((EClassTransitiveInstancesKey) key).getEmfKey(); | ||
788 | baseIndex.removeInstanceListener(Collections.singleton(eClass), | ||
789 | new EClassTransitiveInstancesAdapter(listener, seed.get(0))); | ||
790 | } else if (key instanceof EDataTypeInSlotsKey) { | ||
791 | EDataType dataType = ((EDataTypeInSlotsKey) key).getEmfKey(); | ||
792 | baseIndex.removeDataTypeListener(Collections.singleton(dataType), | ||
793 | new EDataTypeInSlotsAdapter(listener, seed.get(0))); | ||
794 | } else if (key instanceof EStructuralFeatureInstancesKey) { | ||
795 | EStructuralFeature feature = ((EStructuralFeatureInstancesKey) key).getEmfKey(); | ||
796 | baseIndex.removeFeatureListener(Collections.singleton(feature), | ||
797 | new EStructuralFeatureInstancesKeyAdapter(listener, seed.get(0), seed.get(1))); | ||
798 | } else { | ||
799 | illegalInputKey(key); | ||
800 | } | ||
801 | } | ||
802 | |||
803 | // TODO wrap / unwrap enum literals | ||
804 | // TODO use this in all other public methods (maybe wrap & delegate?) | ||
805 | |||
806 | @Override | ||
807 | public Object unwrapElement(Object internalElement) { | ||
808 | return internalElement; | ||
809 | } | ||
810 | @Override | ||
811 | public Tuple unwrapTuple(Tuple internalElements) { | ||
812 | return internalElements; | ||
813 | } | ||
814 | @Override | ||
815 | public Object wrapElement(Object externalElement) { | ||
816 | return externalElement; | ||
817 | } | ||
818 | @Override | ||
819 | public Tuple wrapTuple(Tuple externalElements) { | ||
820 | return externalElements; | ||
821 | } | ||
822 | |||
823 | /** | ||
824 | * @since 1.4 | ||
825 | */ | ||
826 | @Override | ||
827 | public void ensureWildcardIndexing(IndexingService service) { | ||
828 | baseIndex.setWildcardLevel(IndexingLevel.toLevel(service)); | ||
829 | } | ||
830 | |||
831 | /** | ||
832 | * @since 1.4 | ||
833 | */ | ||
834 | @Override | ||
835 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException { | ||
836 | baseIndex.executeAfterTraversal(runnable); | ||
837 | } | ||
838 | } | ||
839 | |||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFScope.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFScope.java new file mode 100644 index 00000000..dead9716 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFScope.java | |||
@@ -0,0 +1,199 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Denes Harmath, 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.emf; | ||
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.function.Predicate; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import org.apache.log4j.Logger; | ||
19 | import org.eclipse.emf.common.notify.Notifier; | ||
20 | import org.eclipse.emf.common.util.URI; | ||
21 | import org.eclipse.emf.ecore.EObject; | ||
22 | import org.eclipse.emf.ecore.resource.Resource; | ||
23 | import org.eclipse.emf.ecore.resource.ResourceSet; | ||
24 | import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; | ||
25 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
26 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
27 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
28 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
29 | import tools.refinery.viatra.runtime.base.api.BaseIndexOptions; | ||
30 | import tools.refinery.viatra.runtime.base.api.NavigationHelper; | ||
31 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
32 | |||
33 | /** | ||
34 | * An {@link QueryScope} consisting of EMF objects contained in multiple {@link ResourceSet}s, a single {@link ResourceSet}, {@link Resource} or a containment subtree below a given {@link EObject}. | ||
35 | * | ||
36 | * <p> The scope is characterized by a root and some options (see {@link BaseIndexOptions}) such as dynamic EMF mode, subtree filtering etc. | ||
37 | * <p> | ||
38 | * The scope of pattern matching will be the given EMF model root(s) and below (see FAQ for more precise definition). | ||
39 | * | ||
40 | * <p> Note on <i>cross-resource containment</i>: in case of {@link EObject} scopes, cross-resource containments will be considered part of the scope. | ||
41 | * The same goes for {@link ResourceSet} scopes, provided that the resource of the contained element is successfully loaded into the resource set. | ||
42 | * In case of a {@link Resource} scope, containments pointing out from the resource will be excluded from the scope. | ||
43 | * Thus the boundaries of {@link EObject} scopes conform to the notion of EMF logical containment, while {@link Resource} and {@link ResourceSet} scopes adhere to persistence boundaries. | ||
44 | * | ||
45 | * @author Bergmann Gabor | ||
46 | * | ||
47 | */ | ||
48 | public class EMFScope extends QueryScope { | ||
49 | |||
50 | private Set<? extends Notifier> scopeRoots; | ||
51 | private BaseIndexOptions options; | ||
52 | |||
53 | /** | ||
54 | * Creates an EMF scope at the given root, with default options (recommended for most users). | ||
55 | * @param scopeRoot the root of the EMF scope | ||
56 | * @throws ViatraQueryRuntimeException- if scopeRoot is not an EMF ResourceSet, Resource or EObject | ||
57 | */ | ||
58 | public EMFScope(Notifier scopeRoot) { | ||
59 | this(Collections.singleton(scopeRoot), new BaseIndexOptions()); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Creates an EMF scope at the given root, with customizable options. | ||
64 | * <p> Most users should consider {@link #EMFScope(Notifier)} instead. | ||
65 | * @param scopeRoot the root of the EMF scope | ||
66 | * @param options the base index building settings | ||
67 | * @throws ViatraQueryRuntimeException if scopeRoot is not an EMF ResourceSet, Resource or EObject | ||
68 | */ | ||
69 | public EMFScope(Notifier scopeRoot, BaseIndexOptions options) { | ||
70 | this(Collections.singleton(scopeRoot), options); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Creates an EMF scope at the given roots, with default options (recommended for most users). | ||
75 | * @param scopeRoots the roots of the EMF scope, must be {@link ResourceSet}s | ||
76 | * @throws ViatraQueryRuntimeException if not all scopeRoots are {@link ResourceSet}s | ||
77 | */ | ||
78 | public EMFScope(Set<? extends ResourceSet> scopeRoots) { | ||
79 | this(scopeRoots, new BaseIndexOptions()); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Creates an EMF scope at the given roots, with customizable options. | ||
84 | * <p> Most users should consider {@link #EMFScope(Set)} instead. | ||
85 | * @param scopeRoots the roots of the EMF scope, must be {@link ResourceSet}s | ||
86 | * @param options the base index building settings | ||
87 | * @throws ViatraQueryRuntimeException if not all scopeRoots are {@link ResourceSet}s | ||
88 | */ | ||
89 | public EMFScope(Set<? extends Notifier> scopeRoots, BaseIndexOptions options) { | ||
90 | super(); | ||
91 | if (scopeRoots.isEmpty()) { | ||
92 | throw new IllegalArgumentException("No scope roots given"); | ||
93 | } else if (scopeRoots.size() == 1) { | ||
94 | checkScopeRoots(scopeRoots, EObject.class::isInstance, Resource.class::isInstance, ResourceSet.class::isInstance); | ||
95 | } else { | ||
96 | checkScopeRoots(scopeRoots, ResourceSet.class::isInstance); | ||
97 | } | ||
98 | this.scopeRoots = new HashSet<>(scopeRoots); | ||
99 | this.options = options.copy(); | ||
100 | } | ||
101 | |||
102 | @SafeVarargs | ||
103 | private final void checkScopeRoots(Set<? extends Notifier> scopeRoots, Predicate<Notifier>... predicates) { | ||
104 | for (Notifier scopeRoot : scopeRoots) { | ||
105 | // Creating compound predicate that checks the various branches of disjunction together | ||
106 | Predicate<Notifier> compoundPredicate = Arrays.stream(predicates).collect(Collectors.reducing(a -> true, a -> a, (a, b) -> a.or(b))); | ||
107 | if (!compoundPredicate.test(scopeRoot)) | ||
108 | throw new ViatraQueryException(ViatraQueryException.INVALID_EMFROOT | ||
109 | + (scopeRoot == null ? "(null)" : scopeRoot.getClass().getName()), | ||
110 | ViatraQueryException.INVALID_EMFROOT_SHORT); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * @return the scope roots ({@link ResourceSet}s) containing the model | ||
116 | */ | ||
117 | public Set<? extends Notifier> getScopeRoots() { | ||
118 | return scopeRoots; | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * @return the options | ||
123 | */ | ||
124 | public BaseIndexOptions getOptions() { | ||
125 | return options.copy(); | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public int hashCode() { | ||
130 | final int prime = 31; | ||
131 | int result = 1; | ||
132 | result = prime * result + ((options == null) ? 0 : options.hashCode()); | ||
133 | result = prime * result | ||
134 | + ((scopeRoots == null) ? 0 : scopeRoots.hashCode()); | ||
135 | return result; | ||
136 | } | ||
137 | @Override | ||
138 | public boolean equals(Object obj) { | ||
139 | if (this == obj) | ||
140 | return true; | ||
141 | if (obj == null) | ||
142 | return false; | ||
143 | if (!(obj instanceof EMFScope)) | ||
144 | return false; | ||
145 | EMFScope other = (EMFScope) obj; | ||
146 | if (options == null) { | ||
147 | if (other.options != null) | ||
148 | return false; | ||
149 | } else if (!options.equals(other.options)) | ||
150 | return false; | ||
151 | if (scopeRoots == null) { | ||
152 | if (other.scopeRoots != null) | ||
153 | return false; | ||
154 | } else if (!scopeRoots.equals(other.scopeRoots)) | ||
155 | return false; | ||
156 | return true; | ||
157 | } | ||
158 | |||
159 | |||
160 | @Override | ||
161 | public String toString() { | ||
162 | return String.format("EMFScope(%s):%s", options, scopeRoots.stream().map(this::scopeRootString).collect(Collectors.joining(","))); | ||
163 | } | ||
164 | |||
165 | private String scopeRootString(Notifier notifier) { | ||
166 | if (notifier instanceof Resource) { | ||
167 | Resource resource = (Resource) notifier; | ||
168 | return String.format("%s(%s)", resource.getClass(), resource.getURI()); | ||
169 | } else if (notifier instanceof ResourceSet) { | ||
170 | ResourceSet resourceSet = (ResourceSet) notifier; | ||
171 | return resourceSet.getResources().stream() | ||
172 | .map(Resource::getURI) | ||
173 | .map(URI::toString) | ||
174 | .collect(Collectors.joining(", ", resourceSet.getClass() + "(", ")")); | ||
175 | } else { | ||
176 | return notifier.toString(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | @Override | ||
181 | protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, Logger logger) { | ||
182 | return new EMFEngineContext(this, engine, errorListener, logger); | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * Provides access to the underlying EMF model index ({@link NavigationHelper}) from a VIATRA Query engine instantiated on an EMFScope | ||
187 | * | ||
188 | * @param engine an already existing VIATRA Query engine instantiated on an EMFScope | ||
189 | * @return the underlying EMF base index that indexes the contents of the EMF model | ||
190 | * @throws ViatraQueryRuntimeException if base index initialization fails | ||
191 | */ | ||
192 | public static NavigationHelper extractUnderlyingEMFIndex(ViatraQueryEngine engine) { | ||
193 | final QueryScope scope = engine.getScope(); | ||
194 | if (scope instanceof EMFScope) | ||
195 | return ((EMFBaseIndexWrapper)AdvancedViatraQueryEngine.from(engine).getBaseIndex()).getNavigationHelper(); | ||
196 | else throw new IllegalArgumentException("Cannot extract EMF base index from VIATRA Query engine instantiated on non-EMF scope " + scope); | ||
197 | } | ||
198 | |||
199 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/helper/ViatraQueryRuntimeHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/helper/ViatraQueryRuntimeHelper.java new file mode 100644 index 00000000..93ac97f2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/helper/ViatraQueryRuntimeHelper.java | |||
@@ -0,0 +1,161 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.emf.helper; | ||
10 | |||
11 | import java.util.function.Function; | ||
12 | |||
13 | import org.eclipse.emf.ecore.EClassifier; | ||
14 | import org.eclipse.emf.ecore.EObject; | ||
15 | import org.eclipse.emf.ecore.EPackage; | ||
16 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
17 | import tools.refinery.viatra.runtime.api.IPatternMatch; | ||
18 | |||
19 | /** | ||
20 | * Helper functions for dealing with the EMF objects with VIATRA Queries. | ||
21 | * | ||
22 | * @author Abel Hegedus | ||
23 | * @since 0.9 | ||
24 | * | ||
25 | */ | ||
26 | public class ViatraQueryRuntimeHelper { | ||
27 | |||
28 | private ViatraQueryRuntimeHelper() {/*Utility class constructor*/} | ||
29 | |||
30 | private static final Function<Object, String> STRING_VALUE_TRANSFORMER = input -> (input == null) ? "(null)" : input.toString(); | ||
31 | |||
32 | /** | ||
33 | * Gives a human-readable name of an EMF type. | ||
34 | */ | ||
35 | public static String prettyPrintEMFType(Object typeObject) { | ||
36 | if (typeObject == null) { | ||
37 | return "(null)"; | ||
38 | } else if (typeObject instanceof EClassifier) { | ||
39 | final EClassifier eClassifier = (EClassifier) typeObject; | ||
40 | final EPackage ePackage = eClassifier.getEPackage(); | ||
41 | final String nsURI = ePackage == null ? null : ePackage.getNsURI(); | ||
42 | final String typeName = eClassifier.getName(); | ||
43 | return "" + nsURI + "/" + typeName; | ||
44 | } else if (typeObject instanceof EStructuralFeature) { | ||
45 | final EStructuralFeature feature = (EStructuralFeature) typeObject; | ||
46 | return prettyPrintEMFType(feature.getEContainingClass()) + "." + feature.getName(); | ||
47 | } else | ||
48 | return typeObject.toString(); | ||
49 | } | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Get the structural feature with the given name of the given object. | ||
54 | * | ||
55 | * @param o | ||
56 | * the object (must be an EObject) | ||
57 | * @param featureName | ||
58 | * the name of the feature | ||
59 | * @return the EStructuralFeature of the object or null if it can not be found | ||
60 | */ | ||
61 | public static EStructuralFeature getFeature(Object o, String featureName) { | ||
62 | if (o instanceof EObject) { | ||
63 | EStructuralFeature feature = ((EObject) o).eClass().getEStructuralFeature(featureName); | ||
64 | return feature; | ||
65 | } | ||
66 | return null; | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Returns the message for the given match using the given format. The format string can refer to the value of | ||
71 | * parameter A of the match with $A$ and even access features of A (if it's an EObject), e.g. $A.id$. | ||
72 | * | ||
73 | * <p/> | ||
74 | * If the selected parameter does not exist, the string "[no such parameter]" is added | ||
75 | * | ||
76 | * <p/> | ||
77 | * If no feature is defined, but A has a feature called "name", then its value is used. | ||
78 | * | ||
79 | * <p/> | ||
80 | * If the selected feature does not exist, A.toString() is added. | ||
81 | * | ||
82 | * <p/> | ||
83 | * If the selected feature is null, the string "null" is added. | ||
84 | * | ||
85 | * @param match | ||
86 | * cannot be null! | ||
87 | * @param messageFormat | ||
88 | * cannot be null! | ||
89 | */ | ||
90 | public static String getMessage(IPatternMatch match, String messageFormat) { | ||
91 | return getMessage(match, messageFormat, STRING_VALUE_TRANSFORMER); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Returns the message for the given match using the given format while transforming values with the given function. | ||
96 | * The format string can refer to the value of parameter A of the match with $A$ and even access features of A (if | ||
97 | * it's an EObject), e.g. $A.id$. The function will be called to compute the final string representation of the | ||
98 | * values selected by the message format. | ||
99 | * | ||
100 | * <p/> | ||
101 | * If the selected parameter does not exist, the string "[no such parameter]" is added | ||
102 | * | ||
103 | * <p/> | ||
104 | * If no feature is defined, but A has a feature called "name", then its value is passed to the function. | ||
105 | * | ||
106 | * <p/> | ||
107 | * If the selected feature does not exist, A is passed to the function. | ||
108 | * | ||
109 | * <p/> | ||
110 | * If the selected feature is null, the string "null" is added. | ||
111 | * | ||
112 | * @param match | ||
113 | * cannot be null! | ||
114 | * @param messageFormat | ||
115 | * cannot be null! | ||
116 | * @param parameterValueTransformer | ||
117 | * cannot be null! | ||
118 | * @since 2.0 | ||
119 | */ | ||
120 | public static String getMessage(IPatternMatch match, String messageFormat, Function<Object,String> parameterValueTransformer) { | ||
121 | String[] tokens = messageFormat.split("\\$"); | ||
122 | StringBuilder newText = new StringBuilder(); | ||
123 | |||
124 | for (int i = 0; i < tokens.length; i++) { | ||
125 | if (i % 2 == 0) { | ||
126 | newText.append(tokens[i]); | ||
127 | } else { | ||
128 | String[] objectTokens = tokens[i].split("\\."); | ||
129 | if (objectTokens.length > 0) { | ||
130 | Object o = null; | ||
131 | EStructuralFeature feature = null; | ||
132 | |||
133 | if (objectTokens.length == 1) { | ||
134 | o = match.get(objectTokens[0]); | ||
135 | feature = getFeature(o, "name"); | ||
136 | } | ||
137 | if (objectTokens.length == 2) { | ||
138 | o = match.get(objectTokens[0]); | ||
139 | feature = getFeature(o, objectTokens[1]); | ||
140 | } | ||
141 | |||
142 | if (o != null && feature != null) { | ||
143 | Object value = ((EObject) o).eGet(feature); | ||
144 | if (value != null) { | ||
145 | newText.append(parameterValueTransformer.apply(value)); | ||
146 | } else { | ||
147 | newText.append("null"); | ||
148 | } | ||
149 | } else if (o != null) { | ||
150 | newText.append(parameterValueTransformer.apply(o)); | ||
151 | } | ||
152 | } else { | ||
153 | newText.append("[no such parameter]"); | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return newText.toString(); | ||
159 | } | ||
160 | |||
161 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/BaseEMFTypeKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/BaseEMFTypeKey.java new file mode 100644 index 00000000..c5dfa966 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/BaseEMFTypeKey.java | |||
@@ -0,0 +1,34 @@ | |||
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.emf.types; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.common.BaseInputKeyWrapper; | ||
12 | |||
13 | /** | ||
14 | * Base class for EMF Type keys. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public abstract class BaseEMFTypeKey<EMFKey> extends BaseInputKeyWrapper<EMFKey> { | ||
19 | |||
20 | public BaseEMFTypeKey(EMFKey emfKey) { | ||
21 | super(emfKey); | ||
22 | } | ||
23 | |||
24 | public EMFKey getEmfKey() { | ||
25 | return getWrappedKey(); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String toString() { | ||
30 | return this.getPrettyPrintableName(); | ||
31 | } | ||
32 | |||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassExactInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassExactInstancesKey.java new file mode 100644 index 00000000..dd10502d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassExactInstancesKey.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.emf.types; | ||
11 | |||
12 | import org.eclipse.emf.ecore.EClass; | ||
13 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
14 | import tools.refinery.viatra.runtime.emf.helper.ViatraQueryRuntimeHelper; | ||
15 | |||
16 | /** | ||
17 | * Instance tuples are of form (x), where x is an eObject instance of the given eClass, but <b>not</b> one of its subclasses, <b>within the scope</b>. | ||
18 | * <p> This input key has the strict semantics that instances must be within the scope. | ||
19 | * | ||
20 | * @noreference This class is not intended to be referenced by clients. Not currently supported by {@link EMFScope}, for internal use only at the time | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * @since 2.1 | ||
24 | */ | ||
25 | public class EClassExactInstancesKey extends BaseEMFTypeKey<EClass> { | ||
26 | |||
27 | public EClassExactInstancesKey(EClass emfKey) { | ||
28 | super(emfKey); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public String getPrettyPrintableName() { | ||
33 | return "(scoped,exact) "+ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public String getStringID() { | ||
38 | return "eClass(scoped,exact)#"+ ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public int getArity() { | ||
43 | return 1; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public boolean isEnumerable() { | ||
48 | return true; | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassTransitiveInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassTransitiveInstancesKey.java new file mode 100644 index 00000000..4ca6b220 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassTransitiveInstancesKey.java | |||
@@ -0,0 +1,47 @@ | |||
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.emf.types; | ||
10 | |||
11 | import org.eclipse.emf.ecore.EClass; | ||
12 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
13 | import tools.refinery.viatra.runtime.emf.helper.ViatraQueryRuntimeHelper; | ||
14 | |||
15 | /** | ||
16 | * Instance tuples are of form (x), where x is an eObject instance of the given eClass or one of its subclasses <b>within the scope</b>. | ||
17 | * <p> As of version 1.6, this input key has the strict semantics that instances must be within the {@link EMFScope}. | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public class EClassTransitiveInstancesKey extends BaseEMFTypeKey<EClass> { | ||
22 | |||
23 | public EClassTransitiveInstancesKey(EClass emfKey) { | ||
24 | super(emfKey); | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getPrettyPrintableName() { | ||
29 | return "(scoped) "+ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public String getStringID() { | ||
34 | return "eClass(scoped)#"+ ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public int getArity() { | ||
39 | return 1; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean isEnumerable() { | ||
44 | return true; | ||
45 | } | ||
46 | |||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassUnscopedTransitiveInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassUnscopedTransitiveInstancesKey.java new file mode 100644 index 00000000..11c5b235 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassUnscopedTransitiveInstancesKey.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.emf.types; | ||
10 | |||
11 | import org.eclipse.emf.ecore.EClass; | ||
12 | import tools.refinery.viatra.runtime.emf.helper.ViatraQueryRuntimeHelper; | ||
13 | |||
14 | /** | ||
15 | * Instance tuples are of form (x), where x is an eObject instance of the given eClass or one of its subclasses <b>regardless whether it is within the scope</b>. | ||
16 | * | ||
17 | * @author Bergmann Gabor | ||
18 | * @since 1.6 | ||
19 | */ | ||
20 | public class EClassUnscopedTransitiveInstancesKey extends BaseEMFTypeKey<EClass> { | ||
21 | |||
22 | public EClassUnscopedTransitiveInstancesKey(EClass emfKey) { | ||
23 | super(emfKey); | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public String getPrettyPrintableName() { | ||
28 | return "(unscoped) "+ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public String getStringID() { | ||
33 | return "eClass(unscoped)#"+ ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public int getArity() { | ||
38 | return 1; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isEnumerable() { | ||
43 | return false; | ||
44 | } | ||
45 | |||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EDataTypeInSlotsKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EDataTypeInSlotsKey.java new file mode 100644 index 00000000..a1cc4863 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EDataTypeInSlotsKey.java | |||
@@ -0,0 +1,48 @@ | |||
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.emf.types; | ||
10 | |||
11 | import org.eclipse.emf.ecore.EDataType; | ||
12 | import tools.refinery.viatra.runtime.emf.helper.ViatraQueryRuntimeHelper; | ||
13 | |||
14 | /** | ||
15 | * Instance tuples are of form (x), where x is an instance of the given eDataType residing at an attribute slot of an eObject in the model. | ||
16 | * @author Bergmann Gabor | ||
17 | * | ||
18 | */ | ||
19 | public class EDataTypeInSlotsKey extends BaseEMFTypeKey<EDataType> { | ||
20 | |||
21 | /** | ||
22 | * @param emfKey | ||
23 | */ | ||
24 | public EDataTypeInSlotsKey(EDataType emfKey) { | ||
25 | super(emfKey); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getPrettyPrintableName() { | ||
30 | return "(Attribute Slot Values: " + ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey) + ")"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getStringID() { | ||
35 | return "slotValue#" + ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int getArity() { | ||
40 | return 1; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isEnumerable() { | ||
45 | return true; | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EStructuralFeatureInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EStructuralFeatureInstancesKey.java new file mode 100644 index 00000000..357f5e7e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EStructuralFeatureInstancesKey.java | |||
@@ -0,0 +1,48 @@ | |||
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.emf.types; | ||
10 | |||
11 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
12 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
13 | import tools.refinery.viatra.runtime.emf.helper.ViatraQueryRuntimeHelper; | ||
14 | |||
15 | /** | ||
16 | * Instance tuples are of form (x, y), where x is an eObject that has y as the value of the given feature (or one of the values in case of multi-valued). | ||
17 | * | ||
18 | * <p> As of version 1.6, this input key has the strict semantics that x must be within the {@link EMFScope}, scoping is <b>not</b> implied for y. | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public class EStructuralFeatureInstancesKey extends BaseEMFTypeKey<EStructuralFeature> { | ||
23 | |||
24 | public EStructuralFeatureInstancesKey(EStructuralFeature emfKey) { | ||
25 | super(emfKey); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getPrettyPrintableName() { | ||
30 | return ViatraQueryRuntimeHelper.prettyPrintEMFType(wrappedKey); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getStringID() { | ||
35 | return "feature#"+ getPrettyPrintableName(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int getArity() { | ||
40 | return 2; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isEnumerable() { | ||
45 | return true; | ||
46 | } | ||
47 | |||
48 | } | ||
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/extensibility/IQueryGroupProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQueryGroupProvider.java new file mode 100644 index 00000000..45594b5b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQueryGroupProvider.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.extensibility; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
15 | |||
16 | /** | ||
17 | * Provider interface for {@link IQueryGroup} instances with added method for | ||
18 | * requesting the set of FQNs for the query specifications in the group. | ||
19 | * | ||
20 | * @author Abel Hegedus | ||
21 | * @since 1.3 | ||
22 | * | ||
23 | */ | ||
24 | public interface IQueryGroupProvider extends IProvider<IQueryGroup> { | ||
25 | |||
26 | /** | ||
27 | * Note that the provider should load the query group class only if the FQNs can not be computed in other ways. | ||
28 | * | ||
29 | * @return the set of query specification FQNs in the group | ||
30 | */ | ||
31 | Set<String> getQuerySpecificationFQNs(); | ||
32 | |||
33 | /** | ||
34 | * Note that the provider should load the query group class only if the FQNs can not be computed in other ways. | ||
35 | * | ||
36 | * @return a set of providers for query specifications in the group | ||
37 | */ | ||
38 | Set<IQuerySpecificationProvider> getQuerySpecificationProviders(); | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQuerySpecificationProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQuerySpecificationProvider.java new file mode 100644 index 00000000..3c9c235d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQuerySpecificationProvider.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.extensibility; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
13 | |||
14 | /** | ||
15 | * Provider interface for {@link IQuerySpecification} instances with added method for | ||
16 | * requesting the FQN for the query specification. | ||
17 | * | ||
18 | * @author Abel Hegedus | ||
19 | * @since 1.3 | ||
20 | * | ||
21 | */ | ||
22 | public interface IQuerySpecificationProvider extends IProvider<IQuerySpecification<?>> { | ||
23 | |||
24 | /** | ||
25 | * Note that the provider will usually not load the query specification class to return the FQN. | ||
26 | * | ||
27 | * @return the fully qualified name of the provided query specification | ||
28 | */ | ||
29 | String getFullyQualifiedName(); | ||
30 | |||
31 | /** | ||
32 | * Returns the name of project providing the specification (or null if not calculable) | ||
33 | */ | ||
34 | String getSourceProjectName(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/PQueryExtensionFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/PQueryExtensionFactory.java new file mode 100644 index 00000000..91e087d7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/PQueryExtensionFactory.java | |||
@@ -0,0 +1,33 @@ | |||
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.extensibility; | ||
10 | |||
11 | import org.eclipse.core.runtime.CoreException; | ||
12 | import org.eclipse.core.runtime.IStatus; | ||
13 | import org.eclipse.core.runtime.Status; | ||
14 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
15 | |||
16 | /** | ||
17 | * An extension factory to access PQuery instances from Query Specifications. | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * | ||
21 | */ | ||
22 | public class PQueryExtensionFactory extends SingletonExtensionFactory { | ||
23 | |||
24 | @Override | ||
25 | public Object create() throws CoreException { | ||
26 | final Object _spec = super.create(); | ||
27 | if (_spec instanceof IQuerySpecification<?>) { | ||
28 | return ((IQuerySpecification<?>) _spec).getInternalQueryRepresentation(); | ||
29 | } | ||
30 | throw new CoreException(new Status(IStatus.ERROR, getBundle().getSymbolicName(), "Cannot instantiate PQuery instance.")); | ||
31 | } | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonExtensionFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonExtensionFactory.java new file mode 100644 index 00000000..29705968 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonExtensionFactory.java | |||
@@ -0,0 +1,62 @@ | |||
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.extensibility; | ||
10 | |||
11 | import java.lang.reflect.Method; | ||
12 | |||
13 | import org.eclipse.core.runtime.CoreException; | ||
14 | import org.eclipse.core.runtime.IConfigurationElement; | ||
15 | import org.eclipse.core.runtime.IExecutableExtension; | ||
16 | import org.eclipse.core.runtime.IExecutableExtensionFactory; | ||
17 | import org.eclipse.core.runtime.IStatus; | ||
18 | import org.eclipse.core.runtime.Platform; | ||
19 | import org.eclipse.core.runtime.Status; | ||
20 | import org.osgi.framework.Bundle; | ||
21 | |||
22 | /** | ||
23 | * Factory to register a static singleton instance in an extension point. | ||
24 | * | ||
25 | * @author Zoltan Ujhelyi | ||
26 | * | ||
27 | */ | ||
28 | public class SingletonExtensionFactory implements IExecutableExtension, IExecutableExtensionFactory { | ||
29 | |||
30 | private String clazzName; | ||
31 | private Bundle bundle; | ||
32 | |||
33 | protected Bundle getBundle() { | ||
34 | return bundle; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Object create() throws CoreException { | ||
39 | try { | ||
40 | final Class<?> clazz = bundle.loadClass(clazzName); | ||
41 | Method method = clazz.getMethod("instance"); | ||
42 | return method.invoke(null); | ||
43 | } catch (Exception e) { | ||
44 | throw new CoreException(new Status(IStatus.ERROR, bundle.getSymbolicName(), "Error loading group " | ||
45 | + clazzName, e)); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void setInitializationData(IConfigurationElement config, String propertyName, Object data) | ||
51 | throws CoreException { | ||
52 | String id = config.getContributor().getName(); | ||
53 | bundle = Platform.getBundle(id); | ||
54 | if (data instanceof String) { | ||
55 | clazzName = (String) data; | ||
56 | } else { | ||
57 | throw new CoreException(new Status(IStatus.ERROR, bundle.getSymbolicName(), | ||
58 | "Unsupported extension initialization data: " + data)); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQueryGroupProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQueryGroupProvider.java new file mode 100644 index 00000000..758b51dd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQueryGroupProvider.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.extensibility; | ||
10 | |||
11 | import java.util.Set; | ||
12 | import java.util.stream.Collectors; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
15 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; | ||
17 | |||
18 | /** | ||
19 | * Provider implementation for storing an existing query group instance. | ||
20 | * | ||
21 | * @author Abel Hegedus | ||
22 | * @since 1.3 | ||
23 | * | ||
24 | */ | ||
25 | public class SingletonQueryGroupProvider extends SingletonInstanceProvider<IQueryGroup> implements IQueryGroupProvider { | ||
26 | |||
27 | /** | ||
28 | * @param instance the instance to wrap | ||
29 | */ | ||
30 | public SingletonQueryGroupProvider(IQueryGroup instance) { | ||
31 | super(instance); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Set<String> getQuerySpecificationFQNs() { | ||
36 | return get().getSpecifications().stream().map(IQuerySpecification::getFullyQualifiedName) | ||
37 | .collect(Collectors.toSet()); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Set<IQuerySpecificationProvider> getQuerySpecificationProviders() { | ||
42 | return get().getSpecifications().stream().map(SingletonQuerySpecificationProvider::new) | ||
43 | .collect(Collectors.toSet()); | ||
44 | } | ||
45 | |||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQuerySpecificationProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQuerySpecificationProvider.java new file mode 100644 index 00000000..f8f3a741 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQuerySpecificationProvider.java | |||
@@ -0,0 +1,42 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.extensibility; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; | ||
13 | |||
14 | /** | ||
15 | * Provider implementation for storing an existing query specification instance. | ||
16 | * | ||
17 | * @author Abel Hegedus | ||
18 | * @since 1.3 | ||
19 | * | ||
20 | */ | ||
21 | public class SingletonQuerySpecificationProvider extends SingletonInstanceProvider<IQuerySpecification<?>> | ||
22 | implements IQuerySpecificationProvider { | ||
23 | |||
24 | /** | ||
25 | * | ||
26 | * @param instance the instance to wrap | ||
27 | */ | ||
28 | public SingletonQuerySpecificationProvider(IQuerySpecification<?> instance) { | ||
29 | super(instance); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public String getFullyQualifiedName() { | ||
34 | return get().getFullyQualifiedName(); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public String getSourceProjectName() { | ||
39 | return null; | ||
40 | } | ||
41 | |||
42 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/ViatraQueryRuntimeConstants.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/ViatraQueryRuntimeConstants.java new file mode 100644 index 00000000..9b0850e4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/ViatraQueryRuntimeConstants.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, 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.extensibility; | ||
10 | |||
11 | /** | ||
12 | * Utility class for Viatra Query runtime constants, such as extension point identifiers. | ||
13 | * | ||
14 | * @author Abel Hegedus | ||
15 | * | ||
16 | */ | ||
17 | public final class ViatraQueryRuntimeConstants { | ||
18 | |||
19 | private ViatraQueryRuntimeConstants() {/* Constructor hidden for utility class */} | ||
20 | |||
21 | // Surrogate query extension | ||
22 | |||
23 | public static final String SURROGATE_QUERY_EXTENSIONID = "tools.refinery.viatra.runtime.surrogatequeryemf"; | ||
24 | |||
25 | |||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSurrogateQueryLoader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSurrogateQueryLoader.java new file mode 100644 index 00000000..af7cdaf1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSurrogateQueryLoader.java | |||
@@ -0,0 +1,148 @@ | |||
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.internal; | ||
10 | |||
11 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkState; | ||
12 | |||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | |||
19 | import org.apache.log4j.Logger; | ||
20 | import org.eclipse.core.runtime.CoreException; | ||
21 | import org.eclipse.core.runtime.IConfigurationElement; | ||
22 | import org.eclipse.core.runtime.Platform; | ||
23 | import org.eclipse.emf.ecore.EClass; | ||
24 | import org.eclipse.emf.ecore.EClassifier; | ||
25 | import org.eclipse.emf.ecore.EPackage; | ||
26 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
27 | import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey; | ||
28 | import tools.refinery.viatra.runtime.extensibility.ViatraQueryRuntimeConstants; | ||
29 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
30 | import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
32 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
33 | |||
34 | /** | ||
35 | * @author Abel Hegedus | ||
36 | * | ||
37 | */ | ||
38 | public class ExtensionBasedSurrogateQueryLoader { | ||
39 | |||
40 | private static final String DUPLICATE_SURROGATE_QUERY = "Duplicate surrogate query definition %s for feature %s of EClass %s in package %s (FQN in map %s, contributing plug-ins %s, plug-in %s)"; | ||
41 | |||
42 | private Map<String, String> contributingPluginOfFeatureMap = new HashMap<>(); | ||
43 | private Map<EStructuralFeature, PQueryProvider> contributedSurrogateQueries; | ||
44 | |||
45 | private static final ExtensionBasedSurrogateQueryLoader INSTANCE = new ExtensionBasedSurrogateQueryLoader(); | ||
46 | |||
47 | /** | ||
48 | * A provider implementation for PQuery instances based on extension elements. It is expected that the getter will only | ||
49 | * @author Zoltan Ujhelyi | ||
50 | * | ||
51 | */ | ||
52 | private static final class PQueryProvider implements IProvider<PQuery> { | ||
53 | |||
54 | private final IConfigurationElement element; | ||
55 | private PQuery query; | ||
56 | |||
57 | public PQueryProvider(IConfigurationElement element) { | ||
58 | this.element = element; | ||
59 | this.query = null; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public PQuery get() { | ||
64 | try { | ||
65 | if (query == null) { | ||
66 | query = (PQuery) element.createExecutableExtension("surrogate-query"); | ||
67 | } | ||
68 | return query; | ||
69 | } catch (CoreException e) { | ||
70 | throw new IllegalArgumentException("Error initializing surrogate query", e); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | public static ExtensionBasedSurrogateQueryLoader instance() { | ||
76 | return INSTANCE; | ||
77 | } | ||
78 | |||
79 | public void loadKnownSurrogateQueriesIntoRegistry() { | ||
80 | Map<EStructuralFeature, PQueryProvider> knownSurrogateQueryFQNs = getSurrogateQueryProviders(); | ||
81 | for (Entry<EStructuralFeature, PQueryProvider> entry : knownSurrogateQueryFQNs.entrySet()) { | ||
82 | final IInputKey inputKey = new EStructuralFeatureInstancesKey(entry.getKey()); | ||
83 | SurrogateQueryRegistry.instance().registerSurrogateQueryForFeature(inputKey, entry.getValue()); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | private Map<EStructuralFeature, PQueryProvider> getSurrogateQueryProviders() { | ||
88 | if(contributedSurrogateQueries != null) { | ||
89 | return contributedSurrogateQueries; | ||
90 | } | ||
91 | contributedSurrogateQueries = new HashMap<>(); | ||
92 | if (Platform.isRunning()) { | ||
93 | for (IConfigurationElement e : Platform.getExtensionRegistry().getConfigurationElementsFor(ViatraQueryRuntimeConstants.SURROGATE_QUERY_EXTENSIONID)) { | ||
94 | if (e.isValid()) { | ||
95 | processExtension(e); | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | return contributedSurrogateQueries; | ||
100 | } | ||
101 | |||
102 | private void processExtension(IConfigurationElement el) { | ||
103 | |||
104 | try { | ||
105 | String packageUri = el.getAttribute("package-nsUri"); | ||
106 | String className = el.getAttribute("class-name"); | ||
107 | String featureName = el.getAttribute("feature-name"); | ||
108 | String queryFqn = el.getAttribute("query-fqn"); | ||
109 | if (queryFqn == null) { | ||
110 | queryFqn = ""; | ||
111 | } | ||
112 | PQueryProvider surrogateQueryProvider = new PQueryProvider(el); | ||
113 | |||
114 | String contributorName = el.getContributor().getName(); | ||
115 | StringBuilder featureIdBuilder = new StringBuilder(); | ||
116 | checkState(packageUri != null, "Package NsURI cannot be null in extension"); | ||
117 | checkState(className != null, "Class name cannot be null in extension"); | ||
118 | checkState(featureName != null, "Feature name cannot be null in extension"); | ||
119 | |||
120 | EPackage pckg = EPackage.Registry.INSTANCE.getEPackage(packageUri); | ||
121 | featureIdBuilder.append(packageUri); | ||
122 | checkState(pckg != null, "Package %s not found! (plug-in %s)", packageUri, contributorName); | ||
123 | |||
124 | EClassifier clsr = pckg.getEClassifier(className); | ||
125 | featureIdBuilder.append("##").append(className); | ||
126 | checkState(clsr instanceof EClass, "EClassifier %s does not exist in package %s! (plug-in %s)", className, packageUri, contributorName); | ||
127 | |||
128 | EClass cls = (EClass) clsr; | ||
129 | EStructuralFeature feature = cls.getEStructuralFeature(featureName); | ||
130 | featureIdBuilder.append("##").append(featureName); | ||
131 | checkState(feature != null, "Feature %s of EClass %s in package %s not found! (plug-in %s)", featureName, className, packageUri, contributorName); | ||
132 | |||
133 | PQueryProvider fqnInMap = contributedSurrogateQueries.get(feature); | ||
134 | if(fqnInMap != null) { | ||
135 | String duplicateSurrogateFormatString = DUPLICATE_SURROGATE_QUERY; | ||
136 | Collection<String> contributorPlugins = Arrays.asList(contributorName, contributingPluginOfFeatureMap.get(featureIdBuilder.toString())); | ||
137 | String duplicateSurrogateMessage = String.format(duplicateSurrogateFormatString, queryFqn, featureName, | ||
138 | className, packageUri, fqnInMap, contributorPlugins, contributorName); | ||
139 | throw new IllegalStateException(duplicateSurrogateMessage); | ||
140 | } | ||
141 | contributedSurrogateQueries.put(feature, surrogateQueryProvider); | ||
142 | contributingPluginOfFeatureMap.put(featureIdBuilder.toString(), contributorName); | ||
143 | } catch (Exception e) { | ||
144 | final Logger logger = Logger.getLogger(SurrogateQueryRegistry.class); | ||
145 | logger.error("Surrogate query registration failed", e); | ||
146 | } | ||
147 | } | ||
148 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSystemDefaultBackendLoader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSystemDefaultBackendLoader.java new file mode 100644 index 00000000..4339b70c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSystemDefaultBackendLoader.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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 | package tools.refinery.viatra.runtime.internal; | ||
10 | |||
11 | import org.eclipse.core.runtime.CoreException; | ||
12 | import org.eclipse.core.runtime.IConfigurationElement; | ||
13 | import org.eclipse.core.runtime.Platform; | ||
14 | import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider; | ||
17 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
18 | |||
19 | /** | ||
20 | * @since 2.0 | ||
21 | */ | ||
22 | public class ExtensionBasedSystemDefaultBackendLoader { | ||
23 | |||
24 | private static final String EXTENSION_ID = "tools.refinery.viatra.runtime.querybackend"; | ||
25 | private static final ExtensionBasedSystemDefaultBackendLoader INSTANCE = new ExtensionBasedSystemDefaultBackendLoader(); | ||
26 | |||
27 | public static ExtensionBasedSystemDefaultBackendLoader instance() { | ||
28 | return INSTANCE; | ||
29 | } | ||
30 | |||
31 | public void loadKnownBackends() { | ||
32 | IQueryBackendFactory defaultBackend = null; | ||
33 | IQueryBackendFactory defaultCachingBackend = null; | ||
34 | IQueryBackendFactory defaultSearchBackend = null; | ||
35 | final IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_ID); | ||
36 | for (IConfigurationElement e : config) { | ||
37 | try { | ||
38 | IQueryBackendFactoryProvider provider = (IQueryBackendFactoryProvider) e | ||
39 | .createExecutableExtension("provider"); | ||
40 | if (provider.isSystemDefaultEngine()) { | ||
41 | defaultBackend = provider.getFactory(); | ||
42 | } | ||
43 | if (provider.isSystemDefaultCachingBackend()) { | ||
44 | defaultCachingBackend = provider.getFactory(); | ||
45 | } | ||
46 | if (provider.isSystemDefaultSearchBackend()) { | ||
47 | defaultSearchBackend = provider.getFactory(); | ||
48 | } | ||
49 | |||
50 | } catch (CoreException ex) { | ||
51 | // In case errors try to continue with the next one | ||
52 | ViatraQueryLoggingUtil.getLogger(getClass()).error( | ||
53 | String.format("Error while initializing backend %s from plugin %s.", | ||
54 | e.getAttribute("backend"), e.getContributor().getName()), ex); | ||
55 | } | ||
56 | } | ||
57 | ViatraQueryEngineOptions.setSystemDefaultBackends(defaultBackend, defaultCachingBackend, defaultSearchBackend); | ||
58 | } | ||
59 | |||
60 | } | ||
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..c2341273 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java | |||
@@ -0,0 +1,705 @@ | |||
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.internal.apiimpl; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.api.*; | ||
14 | import tools.refinery.viatra.runtime.api.impl.BaseMatcher; | ||
15 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | ||
16 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
17 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
18 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
19 | import tools.refinery.viatra.runtime.exception.ViatraQueryException; | ||
20 | import tools.refinery.viatra.runtime.internal.engine.LifecycleProvider; | ||
21 | import tools.refinery.viatra.runtime.internal.engine.ModelUpdateProvider; | ||
22 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
23 | import tools.refinery.viatra.runtime.matchers.backend.*; | ||
24 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueries; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
33 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
34 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
35 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
36 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
37 | import tools.refinery.viatra.runtime.registry.IDefaultRegistryView; | ||
38 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
39 | import tools.refinery.viatra.runtime.registry.QuerySpecificationRegistry; | ||
40 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
41 | |||
42 | import java.lang.ref.WeakReference; | ||
43 | import java.lang.reflect.InvocationTargetException; | ||
44 | import java.util.*; | ||
45 | import java.util.concurrent.Callable; | ||
46 | import java.util.stream.Collectors; | ||
47 | |||
48 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
49 | |||
50 | /** | ||
51 | * A VIATRA Query engine back-end (implementation) | ||
52 | * | ||
53 | * @author Bergmann Gábor | ||
54 | */ | ||
55 | public final class ViatraQueryEngineImpl extends AdvancedViatraQueryEngine | ||
56 | implements IQueryBackendHintProvider, IQueryCacheContext, IQueryResultProviderAccess { | ||
57 | |||
58 | /** | ||
59 | * | ||
60 | */ | ||
61 | private static final String ERROR_ACCESSING_BACKEND = "Error while accessing query evaluator backend"; | ||
62 | /** | ||
63 | * | ||
64 | */ | ||
65 | private static final String QUERY_ON_DISPOSED_ENGINE_MESSAGE = "Cannot evaluate query on disposed engine!"; | ||
66 | /** | ||
67 | * The engine manager responsible for this engine. Null if this engine is unmanaged. | ||
68 | */ | ||
69 | private final ViatraQueryEngineManager manager; | ||
70 | /** | ||
71 | * The model to which the engine is attached. | ||
72 | */ | ||
73 | private final QueryScope scope; | ||
74 | |||
75 | /** | ||
76 | * The context of the engine, provided by the scope. | ||
77 | */ | ||
78 | private IEngineContext engineContext; | ||
79 | |||
80 | /** | ||
81 | * Initialized matchers for each query | ||
82 | */ | ||
83 | private final IMultiLookup<IQuerySpecification<? extends ViatraQueryMatcher<?>>, ViatraQueryMatcher<?>> matchers = | ||
84 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
85 | |||
86 | /** | ||
87 | * The RETE and other pattern matcher implementations of the VIATRA Query Engine. | ||
88 | */ | ||
89 | private volatile Map<IQueryBackendFactory, IQueryBackend> queryBackends = new HashMap<>(); | ||
90 | |||
91 | /** | ||
92 | * The current engine default hints | ||
93 | */ | ||
94 | private final ViatraQueryEngineOptions engineOptions; | ||
95 | |||
96 | /** | ||
97 | * Common query analysis provided to backends | ||
98 | */ | ||
99 | private QueryAnalyzer queryAnalyzer; | ||
100 | |||
101 | /** | ||
102 | * true if message delivery is currently delayed, false otherwise | ||
103 | */ | ||
104 | private boolean delayMessageDelivery = true; | ||
105 | |||
106 | private final LifecycleProvider lifecycleProvider; | ||
107 | private final ModelUpdateProvider modelUpdateProvider; | ||
108 | private Logger logger; | ||
109 | private boolean disposed = false; | ||
110 | |||
111 | /** | ||
112 | * @param manager | ||
113 | * null if unmanaged | ||
114 | * @param scope | ||
115 | * @param engineDefaultHint | ||
116 | * @since 1.4 | ||
117 | */ | ||
118 | public ViatraQueryEngineImpl(ViatraQueryEngineManager manager, QueryScope scope, | ||
119 | ViatraQueryEngineOptions engineOptions) { | ||
120 | super(); | ||
121 | this.manager = manager; | ||
122 | this.scope = scope; | ||
123 | this.lifecycleProvider = new LifecycleProvider(this, getLogger()); | ||
124 | this.modelUpdateProvider = new ModelUpdateProvider(this, getLogger()); | ||
125 | this.engineContext = scope.createEngineContext(this, taintListener, getLogger()); | ||
126 | |||
127 | if (engineOptions != null) { | ||
128 | this.engineOptions = engineOptions; | ||
129 | } else { | ||
130 | this.engineOptions = ViatraQueryEngineOptions.getDefault(); | ||
131 | } | ||
132 | |||
133 | } | ||
134 | |||
135 | /** | ||
136 | * @param manager | ||
137 | * null if unmanaged | ||
138 | * @param scope | ||
139 | * @param engineDefaultHint | ||
140 | */ | ||
141 | public ViatraQueryEngineImpl(ViatraQueryEngineManager manager, QueryScope scope) { | ||
142 | this(manager, scope, ViatraQueryEngineOptions.getDefault()); | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public boolean isUpdatePropagationDelayed() { | ||
147 | return this.delayMessageDelivery; | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public <V> V delayUpdatePropagation(Callable<V> callable) throws InvocationTargetException { | ||
152 | if (!delayMessageDelivery) { | ||
153 | throw new IllegalStateException("Trying to delay propagation while changes are being flushed"); | ||
154 | } | ||
155 | try { | ||
156 | return callable.call(); | ||
157 | } catch (Exception e) { | ||
158 | throw new InvocationTargetException(e); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | @Override | ||
163 | public void flushChanges() { | ||
164 | if (!delayMessageDelivery) { | ||
165 | throw new IllegalStateException("Trying to flush changes while changes are already being flushed"); | ||
166 | } | ||
167 | delayMessageDelivery = false; | ||
168 | try { | ||
169 | for (IQueryBackend backend : this.queryBackends.values()) { | ||
170 | backend.flushUpdates(); | ||
171 | } | ||
172 | } finally { | ||
173 | delayMessageDelivery = true; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | @Override | ||
178 | public Set<? extends ViatraQueryMatcher<? extends IPatternMatch>> getCurrentMatchers() { | ||
179 | return matchers.distinctValuesStream().collect(Collectors.toSet()); | ||
180 | } | ||
181 | |||
182 | @Override | ||
183 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
184 | IQuerySpecification<Matcher> querySpecification) { | ||
185 | return getMatcher(querySpecification, null); | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getMatcher( | ||
190 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalEvaluationHints) { | ||
191 | IMatcherCapability capability = getRequestedCapability(querySpecification, optionalEvaluationHints); | ||
192 | Matcher matcher = doGetExistingMatcher(querySpecification, capability); | ||
193 | if (matcher != null) { | ||
194 | return matcher; | ||
195 | } | ||
196 | matcher = querySpecification.instantiate(); | ||
197 | |||
198 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; | ||
199 | ((QueryResultWrapper) baseMatcher).setBackend(this, | ||
200 | getResultProvider(querySpecification, optionalEvaluationHints), capability); | ||
201 | internalRegisterMatcher(querySpecification, baseMatcher); | ||
202 | return matcher; | ||
203 | } | ||
204 | |||
205 | @Override | ||
206 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher( | ||
207 | IQuerySpecification<Matcher> querySpecification) { | ||
208 | return getExistingMatcher(querySpecification, null); | ||
209 | } | ||
210 | |||
211 | @Override | ||
212 | public <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher getExistingMatcher( | ||
213 | IQuerySpecification<Matcher> querySpecification, QueryEvaluationHint optionalOverrideHints) { | ||
214 | return doGetExistingMatcher(querySpecification, getRequestedCapability(querySpecification, optionalOverrideHints)); | ||
215 | } | ||
216 | |||
217 | @SuppressWarnings("unchecked") | ||
218 | private <Matcher extends ViatraQueryMatcher<? extends IPatternMatch>> Matcher doGetExistingMatcher( | ||
219 | IQuerySpecification<Matcher> querySpecification, IMatcherCapability requestedCapability) { | ||
220 | for (ViatraQueryMatcher<?> matcher : matchers.lookupOrEmpty(querySpecification)) { | ||
221 | BaseMatcher<?> baseMatcher = (BaseMatcher<?>) matcher; | ||
222 | if (baseMatcher.getCapabilities().canBeSubstitute(requestedCapability)) | ||
223 | return (Matcher) matcher; | ||
224 | } | ||
225 | return null; | ||
226 | } | ||
227 | |||
228 | @Override | ||
229 | public ViatraQueryMatcher<? extends IPatternMatch> getMatcher(String patternFQN) { | ||
230 | IQuerySpecificationRegistry registry = QuerySpecificationRegistry.getInstance(); | ||
231 | IDefaultRegistryView view = registry.getDefaultView(); | ||
232 | IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> querySpecification = view | ||
233 | .getEntry(patternFQN).get(); | ||
234 | if (querySpecification != null) { | ||
235 | return getMatcher(querySpecification); | ||
236 | } else { | ||
237 | throw new ViatraQueryException(String.format( | ||
238 | "No matcher could be constructed for the pattern with FQN %s; if the generated matcher class is not available, please access for the first time using getMatcher(IQuerySpecification)", | ||
239 | patternFQN), "No matcher could be constructed for given pattern FQN."); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | @Override | ||
244 | public IBaseIndex getBaseIndex() { | ||
245 | return engineContext.getBaseIndex(); | ||
246 | } | ||
247 | |||
248 | public final Logger getLogger() { | ||
249 | if (logger == null) { | ||
250 | final int hash = System.identityHashCode(this); | ||
251 | logger = Logger.getLogger(ViatraQueryLoggingUtil.getLogger(ViatraQueryEngine.class).getName() + "." + hash); | ||
252 | if (logger == null) | ||
253 | throw new AssertionError( | ||
254 | "Configuration error: unable to create VIATRA Query runtime logger for engine " + hash); | ||
255 | } | ||
256 | return logger; | ||
257 | } | ||
258 | |||
259 | ///////////////// internal stuff ////////////// | ||
260 | private void internalRegisterMatcher(IQuerySpecification<?> querySpecification, ViatraQueryMatcher<?> matcher) { | ||
261 | matchers.addPair(querySpecification, matcher); | ||
262 | lifecycleProvider.matcherInstantiated(matcher); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Provides access to the selected query backend component of the VIATRA Query Engine. | ||
267 | */ | ||
268 | @Override | ||
269 | public IQueryBackend getQueryBackend(IQueryBackendFactory iQueryBackendFactory) { | ||
270 | IQueryBackend iQueryBackend = queryBackends.get(iQueryBackendFactory); | ||
271 | if (iQueryBackend == null) { | ||
272 | // do this first, to make sure the runtime context exists | ||
273 | final IQueryRuntimeContext queryRuntimeContext = engineContext.getQueryRuntimeContext(); | ||
274 | |||
275 | // maybe the backend has been created in the meantime when the indexer was initialized and queried for | ||
276 | // derived features | ||
277 | // no need to instantiate a new backend in that case | ||
278 | iQueryBackend = queryBackends.get(iQueryBackendFactory); | ||
279 | if (iQueryBackend == null) { | ||
280 | |||
281 | // need to instantiate the backend | ||
282 | iQueryBackend = iQueryBackendFactory.create(new IQueryBackendContext() { | ||
283 | |||
284 | @Override | ||
285 | public IQueryRuntimeContext getRuntimeContext() { | ||
286 | return queryRuntimeContext; | ||
287 | } | ||
288 | |||
289 | @Override | ||
290 | public IQueryCacheContext getQueryCacheContext() { | ||
291 | return ViatraQueryEngineImpl.this; | ||
292 | } | ||
293 | |||
294 | @Override | ||
295 | public Logger getLogger() { | ||
296 | return logger; | ||
297 | } | ||
298 | |||
299 | @Override | ||
300 | public IQueryBackendHintProvider getHintProvider() { | ||
301 | return ViatraQueryEngineImpl.this; | ||
302 | } | ||
303 | |||
304 | @Override | ||
305 | public IQueryResultProviderAccess getResultProviderAccess() { | ||
306 | return ViatraQueryEngineImpl.this; | ||
307 | } | ||
308 | |||
309 | @Override | ||
310 | public QueryAnalyzer getQueryAnalyzer() { | ||
311 | if (queryAnalyzer == null) | ||
312 | queryAnalyzer = new QueryAnalyzer(queryRuntimeContext.getMetaContext()); | ||
313 | return queryAnalyzer; | ||
314 | } | ||
315 | |||
316 | @Override | ||
317 | public boolean areUpdatesDelayed() { | ||
318 | return ViatraQueryEngineImpl.this.delayMessageDelivery; | ||
319 | } | ||
320 | |||
321 | @Override | ||
322 | public IMatcherCapability getRequiredMatcherCapability(PQuery query, | ||
323 | QueryEvaluationHint hint) { | ||
324 | return engineOptions.getQueryBackendFactory(hint).calculateRequiredCapability(query, hint); | ||
325 | } | ||
326 | |||
327 | |||
328 | |||
329 | }); | ||
330 | queryBackends.put(iQueryBackendFactory, iQueryBackend); | ||
331 | } | ||
332 | } | ||
333 | return iQueryBackend; | ||
334 | } | ||
335 | |||
336 | ///////////////// advanced stuff ///////////// | ||
337 | |||
338 | @Override | ||
339 | public void dispose() { | ||
340 | if (manager != null) { | ||
341 | throw new UnsupportedOperationException( | ||
342 | String.format("Cannot dispose() managed VIATRA Query Engine. Attempted for scope %s.", scope)); | ||
343 | } | ||
344 | wipe(); | ||
345 | |||
346 | this.disposed = true; | ||
347 | |||
348 | // called before base index disposal to allow removal of base listeners | ||
349 | lifecycleProvider.engineDisposed(); | ||
350 | |||
351 | try { | ||
352 | engineContext.dispose(); | ||
353 | } catch (IllegalStateException ex) { | ||
354 | getLogger().warn( | ||
355 | "The base index could not be disposed along with the VIATRA Query engine, as there are still active listeners on it."); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | @Override | ||
360 | public void wipe() { | ||
361 | if (manager != null) { | ||
362 | throw new UnsupportedOperationException( | ||
363 | String.format("Cannot wipe() managed VIATRA Query Engine. Attempted for scope %s.", scope)); | ||
364 | } | ||
365 | if (queryBackends != null) { | ||
366 | for (IQueryBackend backend : queryBackends.values()) { | ||
367 | backend.dispose(); | ||
368 | } | ||
369 | queryBackends.clear(); | ||
370 | } | ||
371 | matchers.clear(); | ||
372 | queryAnalyzer = null; | ||
373 | lifecycleProvider.engineWiped(); | ||
374 | } | ||
375 | |||
376 | /** | ||
377 | * Indicates whether the engine is in a tainted, inconsistent state. | ||
378 | */ | ||
379 | private boolean tainted = false; | ||
380 | private IIndexingErrorListener taintListener = new SelfTaintListener(this); | ||
381 | |||
382 | private static class SelfTaintListener implements IIndexingErrorListener { | ||
383 | WeakReference<ViatraQueryEngineImpl> queryEngineRef; | ||
384 | |||
385 | public SelfTaintListener(ViatraQueryEngineImpl queryEngine) { | ||
386 | this.queryEngineRef = new WeakReference<ViatraQueryEngineImpl>(queryEngine); | ||
387 | } | ||
388 | |||
389 | public void engineBecameTainted(String description, Throwable t) { | ||
390 | final ViatraQueryEngineImpl queryEngine = queryEngineRef.get(); | ||
391 | if (queryEngine != null) { | ||
392 | queryEngine.tainted = true; | ||
393 | queryEngine.lifecycleProvider.engineBecameTainted(description, t); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | private boolean noTaintDetectedYet = true; | ||
398 | |||
399 | protected void notifyTainted(String description, Throwable t) { | ||
400 | if (noTaintDetectedYet) { | ||
401 | noTaintDetectedYet = false; | ||
402 | engineBecameTainted(description, t); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | @Override | ||
407 | public void error(String description, Throwable t) { | ||
408 | // Errors does not mean tainting | ||
409 | } | ||
410 | |||
411 | @Override | ||
412 | public void fatal(String description, Throwable t) { | ||
413 | notifyTainted(description, t); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | @Override | ||
418 | public boolean isTainted() { | ||
419 | return tainted; | ||
420 | } | ||
421 | |||
422 | @Override | ||
423 | public boolean isManaged() { | ||
424 | return manager != null; | ||
425 | // return isAdvanced; ??? | ||
426 | } | ||
427 | |||
428 | private <Match extends IPatternMatch> IQueryResultProvider getUnderlyingResultProvider( | ||
429 | final BaseMatcher<Match> matcher) { | ||
430 | // IQueryResultProvider resultProvider = reteEngine.accessMatcher(matcher.getSpecification()); | ||
431 | return matcher.backend; | ||
432 | } | ||
433 | |||
434 | @Override | ||
435 | public <Match extends IPatternMatch> void addMatchUpdateListener(final ViatraQueryMatcher<Match> matcher, | ||
436 | final IMatchUpdateListener<? super Match> listener, boolean fireNow) { | ||
437 | |||
438 | checkArgument(listener != null, "Cannot add null listener!"); | ||
439 | checkArgument(matcher.getEngine() == this, "Cannot register listener for matcher of different engine!"); | ||
440 | checkArgument(!disposed, "Cannot register listener on matcher of disposed engine!"); | ||
441 | |||
442 | final BaseMatcher<Match> bm = (BaseMatcher<Match>) matcher; | ||
443 | |||
444 | final IUpdateable updateDispatcher = (updateElement, isInsertion) -> { | ||
445 | Match match = null; | ||
446 | try { | ||
447 | match = bm.newMatch(updateElement.getElements()); | ||
448 | if (isInsertion) | ||
449 | listener.notifyAppearance(match); | ||
450 | else | ||
451 | listener.notifyDisappearance(match); | ||
452 | } catch (Throwable e) { // NOPMD | ||
453 | if (e instanceof Error) | ||
454 | throw (Error) e; | ||
455 | logger.warn( | ||
456 | String.format( | ||
457 | "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)", | ||
458 | match == null ? "preparing" : "invoking", isInsertion ? "insertion" : "removal", | ||
459 | match == null ? updateElement.toString() : match.prettyPrint(), | ||
460 | matcher.getPatternName(), e.getMessage(), e.getClass().getSimpleName(), listener), | ||
461 | e); | ||
462 | } | ||
463 | |||
464 | }; | ||
465 | |||
466 | IQueryResultProvider resultProvider = getUnderlyingResultProvider(bm); | ||
467 | resultProvider.addUpdateListener(updateDispatcher, listener, fireNow); | ||
468 | } | ||
469 | |||
470 | @Override | ||
471 | public <Match extends IPatternMatch> void removeMatchUpdateListener(ViatraQueryMatcher<Match> matcher, | ||
472 | IMatchUpdateListener<? super Match> listener) { | ||
473 | checkArgument(listener != null, "Cannot remove null listener!"); | ||
474 | checkArgument(matcher.getEngine() == this, "Cannot remove listener from matcher of different engine!"); | ||
475 | checkArgument(!disposed, "Cannot remove listener from matcher of disposed engine!"); | ||
476 | |||
477 | final BaseMatcher<Match> bm = (BaseMatcher<Match>) matcher; | ||
478 | |||
479 | try { | ||
480 | IQueryResultProvider resultProvider = getUnderlyingResultProvider(bm); | ||
481 | resultProvider.removeUpdateListener(listener); | ||
482 | } catch (Exception e) { | ||
483 | logger.error( | ||
484 | "Error while removing listener " + listener + " from the matcher of " + matcher.getPatternName(), | ||
485 | e); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | @Override | ||
490 | public void addModelUpdateListener(ViatraQueryModelUpdateListener listener) { | ||
491 | modelUpdateProvider.addListener(listener); | ||
492 | } | ||
493 | |||
494 | @Override | ||
495 | public void removeModelUpdateListener(ViatraQueryModelUpdateListener listener) { | ||
496 | modelUpdateProvider.removeListener(listener); | ||
497 | } | ||
498 | |||
499 | @Override | ||
500 | public void addLifecycleListener(ViatraQueryEngineLifecycleListener listener) { | ||
501 | lifecycleProvider.addListener(listener); | ||
502 | } | ||
503 | |||
504 | @Override | ||
505 | public void removeLifecycleListener(ViatraQueryEngineLifecycleListener listener) { | ||
506 | lifecycleProvider.removeListener(listener); | ||
507 | } | ||
508 | |||
509 | /** | ||
510 | * Returns an internal interface towards the query backend to feed the matcher with results. | ||
511 | * | ||
512 | * @param query | ||
513 | * the pattern for which the result provider should be delivered | ||
514 | * | ||
515 | * @throws ViatraQueryRuntimeException | ||
516 | */ | ||
517 | public IQueryResultProvider getResultProvider(IQuerySpecification<?> query) { | ||
518 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
519 | |||
520 | return getResultProviderInternal(query, null); | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * Returns an internal interface towards the query backend to feed the matcher with results. | ||
525 | * | ||
526 | * @param query | ||
527 | * the pattern for which the result provider should be delivered | ||
528 | * | ||
529 | * @throws ViatraQueryRuntimeException | ||
530 | */ | ||
531 | public IQueryResultProvider getResultProvider(IQuerySpecification<?> query, QueryEvaluationHint hint) { | ||
532 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
533 | |||
534 | return getResultProviderInternal(query, hint); | ||
535 | } | ||
536 | |||
537 | /** | ||
538 | * This method returns the result provider exactly as described by the passed hint. Query cannot be null! Use | ||
539 | * {@link #getQueryEvaluationHint(IQuerySpecification, QueryEvaluationHint)} before passing a hint to this method to | ||
540 | * make sure engine and query specific hints are correctly applied. | ||
541 | * | ||
542 | * @throws ViatraQueryRuntimeException | ||
543 | */ | ||
544 | private IQueryResultProvider getResultProviderInternal(IQuerySpecification<?> query, QueryEvaluationHint hint) { | ||
545 | return getResultProviderInternal(query.getInternalQueryRepresentation(), hint); | ||
546 | } | ||
547 | |||
548 | /** | ||
549 | * This method returns the result provider exactly as described by the passed hint. Query cannot be null! Use | ||
550 | * {@link #getQueryEvaluationHint(IQuerySpecification, QueryEvaluationHint)} before passing a hint to this method to | ||
551 | * make sure engine and query specific hints are correctly applied. | ||
552 | * | ||
553 | * @throws ViatraQueryRuntimeException | ||
554 | */ | ||
555 | private IQueryResultProvider getResultProviderInternal(PQuery query, QueryEvaluationHint hint) { | ||
556 | Preconditions.checkArgument(query != null, "Query cannot be null!"); | ||
557 | Preconditions.checkArgument(query.getStatus() != PQueryStatus.ERROR, "Cannot initialize a result provider for the erronoues query `%s`.", query.getSimpleName()); | ||
558 | final IQueryBackend backend = getQueryBackend(engineOptions.getQueryBackendFactory(getQueryEvaluationHint(query, hint))); | ||
559 | return backend.getResultProvider(query, hint); | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * Returns the query backend (influenced by the hint system), even if it is a non-caching backend. | ||
564 | * | ||
565 | * @throws ViatraQueryRuntimeException | ||
566 | */ | ||
567 | private IQueryBackend getQueryBackend(PQuery query) { | ||
568 | final IQueryBackendFactory factory = engineOptions.getQueryBackendFactory(getQueryEvaluationHint(query)); | ||
569 | return getQueryBackend(factory); | ||
570 | } | ||
571 | |||
572 | /** | ||
573 | * Returns a caching query backend (influenced by the hint system). | ||
574 | * | ||
575 | * @throws ViatraQueryRuntimeException | ||
576 | */ | ||
577 | private IQueryBackend getCachingQueryBackend(PQuery query) { | ||
578 | IQueryBackend regularBackend = getQueryBackend(query); | ||
579 | if (regularBackend.isCaching()) | ||
580 | return regularBackend; | ||
581 | else | ||
582 | return getQueryBackend(engineOptions.getDefaultCachingBackendFactory()); | ||
583 | } | ||
584 | |||
585 | @Override | ||
586 | public boolean isResultCached(PQuery query) { | ||
587 | try { | ||
588 | return null != getCachingQueryBackend(query).peekExistingResultProvider(query); | ||
589 | } catch (ViatraQueryException iqe) { | ||
590 | getLogger().error(ERROR_ACCESSING_BACKEND, iqe); | ||
591 | return false; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | @Override | ||
596 | public IQueryResultProvider getCachingResultProvider(PQuery query) { | ||
597 | try { | ||
598 | return getCachingQueryBackend(query).getResultProvider(query); | ||
599 | } catch (ViatraQueryException iqe) { | ||
600 | getLogger().error(ERROR_ACCESSING_BACKEND, iqe); | ||
601 | throw iqe; | ||
602 | } | ||
603 | } | ||
604 | |||
605 | private QueryEvaluationHint getEngineDefaultHint() { | ||
606 | return engineOptions.getEngineDefaultHints(); | ||
607 | } | ||
608 | |||
609 | @Override | ||
610 | public QueryEvaluationHint getQueryEvaluationHint(PQuery query) { | ||
611 | return getEngineDefaultHint().overrideBy(query.getEvaluationHints()); | ||
612 | } | ||
613 | |||
614 | private QueryEvaluationHint getQueryEvaluationHint(IQuerySpecification<?> querySpecification, | ||
615 | QueryEvaluationHint optionalOverrideHints) { | ||
616 | return getQueryEvaluationHint(querySpecification.getInternalQueryRepresentation()) | ||
617 | .overrideBy(optionalOverrideHints); | ||
618 | } | ||
619 | |||
620 | private QueryEvaluationHint getQueryEvaluationHint(PQuery query, QueryEvaluationHint optionalOverrideHints) { | ||
621 | return getQueryEvaluationHint(query).overrideBy(optionalOverrideHints); | ||
622 | } | ||
623 | |||
624 | private IMatcherCapability getRequestedCapability(IQuerySpecification<?> querySpecification, | ||
625 | QueryEvaluationHint optionalOverrideHints) { | ||
626 | final QueryEvaluationHint hint = getQueryEvaluationHint(querySpecification, optionalOverrideHints); | ||
627 | return engineOptions.getQueryBackendFactory(hint) | ||
628 | .calculateRequiredCapability(querySpecification.getInternalQueryRepresentation(), hint); | ||
629 | } | ||
630 | |||
631 | @Override | ||
632 | public void prepareGroup(IQueryGroup queryGroup, final QueryEvaluationHint optionalEvaluationHints) { | ||
633 | try { | ||
634 | Preconditions.checkState(!disposed, QUERY_ON_DISPOSED_ENGINE_MESSAGE); | ||
635 | |||
636 | final Set<IQuerySpecification<?>> specifications = new HashSet<IQuerySpecification<?>>( | ||
637 | queryGroup.getSpecifications()); | ||
638 | final Collection<PQuery> patterns = specifications.stream().map( | ||
639 | IQuerySpecification::getInternalQueryRepresentation).collect(Collectors.toList()); | ||
640 | patterns.forEach(PQuery::ensureInitialized); | ||
641 | |||
642 | Collection<String> erroneousPatterns = patterns.stream(). | ||
643 | filter(PQueries.queryStatusPredicate(PQueryStatus.ERROR)). | ||
644 | map(PQuery::getFullyQualifiedName). | ||
645 | collect(Collectors.toList()); | ||
646 | Preconditions.checkState(erroneousPatterns.isEmpty(), "Erroneous query(s) found: %s", | ||
647 | erroneousPatterns.stream().collect(Collectors.joining(", "))); | ||
648 | |||
649 | // TODO maybe do some smarter preparation per backend? | ||
650 | try { | ||
651 | engineContext.getBaseIndex().coalesceTraversals(new Callable<Void>() { | ||
652 | @Override | ||
653 | public Void call() throws Exception { | ||
654 | for (IQuerySpecification<?> query : specifications) { | ||
655 | getResultProviderInternal(query, optionalEvaluationHints); | ||
656 | } | ||
657 | return null; | ||
658 | } | ||
659 | }); | ||
660 | } catch (InvocationTargetException ex) { | ||
661 | final Throwable cause = ex.getCause(); | ||
662 | if (cause instanceof QueryProcessingException) | ||
663 | throw (QueryProcessingException) cause; | ||
664 | if (cause instanceof ViatraQueryException) | ||
665 | throw (ViatraQueryException) cause; | ||
666 | if (cause instanceof RuntimeException) | ||
667 | throw (RuntimeException) cause; | ||
668 | assert (false); | ||
669 | } | ||
670 | } catch (QueryProcessingException e) { | ||
671 | throw new ViatraQueryException(e); | ||
672 | } | ||
673 | } | ||
674 | |||
675 | @Override | ||
676 | public QueryScope getScope() { | ||
677 | return scope; | ||
678 | } | ||
679 | |||
680 | @Override | ||
681 | public ViatraQueryEngineOptions getEngineOptions() { | ||
682 | return engineOptions; | ||
683 | } | ||
684 | |||
685 | @Override | ||
686 | public IQueryResultProvider getResultProviderOfMatcher(ViatraQueryMatcher<? extends IPatternMatch> matcher) { | ||
687 | return ((QueryResultWrapper) matcher).backend; | ||
688 | } | ||
689 | |||
690 | @Override | ||
691 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints) { | ||
692 | try { | ||
693 | return getResultProviderInternal(query, overrideHints); | ||
694 | } catch (ViatraQueryException e) { | ||
695 | getLogger().error(ERROR_ACCESSING_BACKEND, e); | ||
696 | throw e; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | @Override | ||
701 | public boolean isDisposed() { | ||
702 | return disposed; | ||
703 | } | ||
704 | |||
705 | } | ||
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/registry/ExtensionBasedQuerySpecificationLoader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/ExtensionBasedQuerySpecificationLoader.java new file mode 100644 index 00000000..40bf3e67 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/ExtensionBasedQuerySpecificationLoader.java | |||
@@ -0,0 +1,303 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Objects; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import org.eclipse.core.runtime.CoreException; | ||
18 | import org.eclipse.core.runtime.IConfigurationElement; | ||
19 | import org.eclipse.core.runtime.Platform; | ||
20 | import tools.refinery.viatra.runtime.IExtensions; | ||
21 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
22 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
23 | import tools.refinery.viatra.runtime.extensibility.IQueryGroupProvider; | ||
24 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
28 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
29 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
30 | |||
31 | /** | ||
32 | * Loader for the {@link QuerySpecificationRegistry} based on the query group extensions generated by the VIATRA Query | ||
33 | * builder. The loader has a single instance that processes the extensions on demand if the platform is running, caches | ||
34 | * the results and updates the {@link QuerySpecificationRegistry}. Note that the loader does not perform class loading | ||
35 | * on the query group if possible. | ||
36 | * | ||
37 | * <p> | ||
38 | * The class has a single instance accessible with {@link #getInstance()}. | ||
39 | * | ||
40 | * @author Abel Hegedus | ||
41 | * @since 1.3 | ||
42 | * | ||
43 | */ | ||
44 | public class ExtensionBasedQuerySpecificationLoader { | ||
45 | |||
46 | public static final String CONNECTOR_ID = "tools.refinery.viatra.runtime.querygroup.extension.based.connector"; | ||
47 | |||
48 | private static final String DUPLICATE_QUERY_GROUP_MESSAGE = "Duplicate query group identifier %s for plugin %s (already contributed by %s)"; | ||
49 | private static final ExtensionBasedQuerySpecificationLoader INSTANCE = new ExtensionBasedQuerySpecificationLoader(); | ||
50 | |||
51 | private IMultiLookup<String, String> contributingPluginOfGroupMap = | ||
52 | CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
53 | private Map<String, QueryGroupProvider> contributedQueryGroups; | ||
54 | |||
55 | private ExtensionBasedSourceConnector sourceConnector; | ||
56 | |||
57 | |||
58 | /** | ||
59 | * @return the single instance of the loader. | ||
60 | */ | ||
61 | public static ExtensionBasedQuerySpecificationLoader getInstance() { | ||
62 | return INSTANCE; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Loads the query specifications that are registered through extension points into the | ||
67 | * {@link QuerySpecificationRegistry}. | ||
68 | */ | ||
69 | public void loadRegisteredQuerySpecificationsIntoRegistry() { | ||
70 | ((QuerySpecificationRegistry) QuerySpecificationRegistry.getInstance()).addDelayedSourceConnector(getSourceConnector()); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Return a source connector that can be used to load query specifications contributed through | ||
75 | * extensions into a {@link IQuerySpecificationRegistry}. | ||
76 | * | ||
77 | * @return the source connector | ||
78 | */ | ||
79 | public IRegistrySourceConnector getSourceConnector() { | ||
80 | if (this.sourceConnector == null) { | ||
81 | this.sourceConnector = new ExtensionBasedSourceConnector(); | ||
82 | } | ||
83 | return sourceConnector; | ||
84 | } | ||
85 | |||
86 | private Map<String, QueryGroupProvider> getRegisteredQueryGroups() { | ||
87 | if(contributedQueryGroups != null) { | ||
88 | return contributedQueryGroups; | ||
89 | } | ||
90 | contributedQueryGroups = new HashMap<>(); | ||
91 | if (Platform.isRunning()) { | ||
92 | for (IConfigurationElement e : Platform.getExtensionRegistry().getConfigurationElementsFor(IExtensions.QUERY_SPECIFICATION_EXTENSION_POINT_ID)) { | ||
93 | if (e.isValid()) { | ||
94 | processExtension(e); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | return contributedQueryGroups; | ||
99 | } | ||
100 | |||
101 | private void processExtension(IConfigurationElement el) { | ||
102 | String id = null; | ||
103 | try { | ||
104 | String contributorName = el.getContributor().getName(); | ||
105 | id = el.getAttribute("id"); | ||
106 | if(id == null) { | ||
107 | throw new IllegalStateException(String.format("Query group extension identifier is required (plug-in: %s)!", contributorName)); | ||
108 | } | ||
109 | |||
110 | QueryGroupProvider provider = new QueryGroupProvider(el); | ||
111 | |||
112 | QueryGroupProvider queryGroupInMap = contributedQueryGroups.get(id); | ||
113 | if(queryGroupInMap != null) { | ||
114 | IMemoryView<String> contributorPlugins = contributingPluginOfGroupMap.lookupOrEmpty(id); | ||
115 | throw new IllegalStateException(String.format(DUPLICATE_QUERY_GROUP_MESSAGE, id, contributorName, contributorPlugins.distinctValues())); | ||
116 | } | ||
117 | |||
118 | contributedQueryGroups.put(id, provider); | ||
119 | contributingPluginOfGroupMap.addPair(id, contributorName); | ||
120 | } catch (Exception e) { | ||
121 | // If there are serious compilation errors in the file loaded by the query registry, an error is thrown | ||
122 | if (id == null) { | ||
123 | id = "undefined in plugin.xml"; | ||
124 | } | ||
125 | ViatraQueryLoggingUtil.getLogger(ExtensionBasedQuerySpecificationLoader.class).error( | ||
126 | "[ExtensionBasedQuerySpecificationLoader] Exception during query specification registry initialization when preparing group: " | ||
127 | + id + "! " + e.getMessage(), e); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * @author Abel Hegedus | ||
133 | * | ||
134 | */ | ||
135 | private final class ExtensionBasedSourceConnector implements IRegistrySourceConnector { | ||
136 | |||
137 | private Set<IConnectorListener> listeners; | ||
138 | |||
139 | public ExtensionBasedSourceConnector() { | ||
140 | this.listeners = new HashSet<>(); | ||
141 | } | ||
142 | |||
143 | @Override | ||
144 | public String getIdentifier() { | ||
145 | return ExtensionBasedQuerySpecificationLoader.CONNECTOR_ID; | ||
146 | } | ||
147 | |||
148 | @Override | ||
149 | public void addListener(IConnectorListener listener) { | ||
150 | Objects.requireNonNull(listener, "Listener must not be null!"); | ||
151 | boolean added = listeners.add(listener); | ||
152 | if(added) { | ||
153 | for (QueryGroupProvider queryGroupProvider : getRegisteredQueryGroups().values()) { | ||
154 | for (IQuerySpecificationProvider specificationProvider : queryGroupProvider.getQuerySpecificationProviders()) { | ||
155 | listener.querySpecificationAdded(this, specificationProvider); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public void removeListener(IConnectorListener listener) { | ||
163 | Objects.requireNonNull(listener, "Listener must not be null!"); | ||
164 | listeners.remove(listener); | ||
165 | } | ||
166 | |||
167 | @Override | ||
168 | public boolean includeSpecificationsInDefaultViews() { | ||
169 | return true; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * Provider implementation that uses the group extension to load the query group on-demand. | ||
175 | * It also provides the set of query FQNs that are part of the group without class loading. | ||
176 | * Once loaded, the query group is cached for future use. | ||
177 | * | ||
178 | * @author Abel Hegedus | ||
179 | */ | ||
180 | private static final class QueryGroupProvider implements IQueryGroupProvider { | ||
181 | |||
182 | private static final String DUPLICATE_FQN_MESSAGE = "Duplicate FQN %s in query group extension point (plug-in %s)"; | ||
183 | private final IConfigurationElement element; | ||
184 | private IQueryGroup queryGroup; | ||
185 | private Set<String> querySpecificationFQNs; | ||
186 | private Map<String, IQuerySpecificationProvider> querySpecificationMap; | ||
187 | |||
188 | public QueryGroupProvider(IConfigurationElement element) { | ||
189 | this.element = element; | ||
190 | this.queryGroup = null; | ||
191 | this.querySpecificationFQNs = null; | ||
192 | this.querySpecificationMap = null; | ||
193 | } | ||
194 | |||
195 | @Override | ||
196 | public IQueryGroup get() { | ||
197 | try{ | ||
198 | if(queryGroup == null) { | ||
199 | queryGroup = (IQueryGroup) element.createExecutableExtension("group"); | ||
200 | } | ||
201 | return queryGroup; | ||
202 | } catch (CoreException e) { | ||
203 | throw new IllegalStateException(e.getMessage(), e); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | @Override | ||
208 | public Set<String> getQuerySpecificationFQNs() { | ||
209 | if(querySpecificationFQNs == null) { | ||
210 | Set<String> fqns = new HashSet<>(); | ||
211 | for (IConfigurationElement e : element.getChildren("query-specification")) { | ||
212 | if (e.isValid()) { | ||
213 | String fqn = e.getAttribute("fqn"); | ||
214 | boolean added = fqns.add(fqn); | ||
215 | if(!added) { | ||
216 | String contributorName = e.getContributor().getName(); | ||
217 | throw new IllegalArgumentException(String.format(DUPLICATE_FQN_MESSAGE,fqn, contributorName)); | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | if(fqns.isEmpty()) { | ||
222 | // we must load the class and get the specifications | ||
223 | IQueryGroup loadedQueryGroup = get(); | ||
224 | for (IQuerySpecification<?> specification : loadedQueryGroup.getSpecifications()) { | ||
225 | String fullyQualifiedName = specification.getFullyQualifiedName(); | ||
226 | boolean added = fqns.add(fullyQualifiedName); | ||
227 | if(!added) { | ||
228 | String contributorName = element.getContributor().getName(); | ||
229 | throw new IllegalArgumentException(String.format(DUPLICATE_FQN_MESSAGE, fullyQualifiedName, contributorName)); | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | // we will never change the set after initialization | ||
234 | querySpecificationFQNs = new HashSet<>(fqns); | ||
235 | } | ||
236 | return querySpecificationFQNs; | ||
237 | } | ||
238 | |||
239 | @Override | ||
240 | public Set<IQuerySpecificationProvider> getQuerySpecificationProviders() { | ||
241 | return new HashSet<>(getQuerySpecificationMap().values()); | ||
242 | } | ||
243 | |||
244 | private Map<String, IQuerySpecificationProvider> getQuerySpecificationMap() { | ||
245 | if(querySpecificationMap == null){ | ||
246 | querySpecificationMap = new HashMap<>(); | ||
247 | Set<String> fqns = getQuerySpecificationFQNs(); | ||
248 | for (String fqn : fqns) { | ||
249 | querySpecificationMap.put(fqn, new GroupBasedQuerySpecificationProvider(fqn, this)); | ||
250 | } | ||
251 | } | ||
252 | return querySpecificationMap; | ||
253 | } | ||
254 | |||
255 | } | ||
256 | |||
257 | /** | ||
258 | * Provider implementation that uses the query group extension to load a query specification by its FQN. Note that | ||
259 | * the FQN of the provided query specification is set with the constructor and can be requested without loading the | ||
260 | * class. Once loaded, the query specification is cached for future use. | ||
261 | * | ||
262 | * @author Abel Hegedus | ||
263 | * | ||
264 | */ | ||
265 | private static final class GroupBasedQuerySpecificationProvider implements IQuerySpecificationProvider { | ||
266 | |||
267 | private String queryFQN; | ||
268 | private QueryGroupProvider queryGroupProvider; | ||
269 | private IQuerySpecification<?> specification; | ||
270 | |||
271 | public GroupBasedQuerySpecificationProvider(String queryFQN, QueryGroupProvider queryGroupProvider) { | ||
272 | this.queryFQN = queryFQN; | ||
273 | this.queryGroupProvider = queryGroupProvider; | ||
274 | this.specification = null; | ||
275 | } | ||
276 | |||
277 | @Override | ||
278 | public IQuerySpecification<?> get() { | ||
279 | if(specification == null) { | ||
280 | if(queryGroupProvider.getQuerySpecificationFQNs().contains(queryFQN)) { | ||
281 | for (IQuerySpecification<?> spec : queryGroupProvider.get().getSpecifications()) { | ||
282 | if(spec.getFullyQualifiedName().equals(queryFQN)){ | ||
283 | this.specification = spec; | ||
284 | } | ||
285 | } | ||
286 | } else { | ||
287 | throw new IllegalStateException(String.format("Could not find query specifition %s in group (plug-in %s)", queryFQN, queryGroupProvider.element.getContributor().getName())); | ||
288 | } | ||
289 | } | ||
290 | return specification; | ||
291 | } | ||
292 | |||
293 | @Override | ||
294 | public String getFullyQualifiedName() { | ||
295 | return queryFQN; | ||
296 | } | ||
297 | |||
298 | @Override | ||
299 | public String getSourceProjectName() { | ||
300 | return queryGroupProvider.element.getContributor().getName(); | ||
301 | } | ||
302 | } | ||
303 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IConnectorListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IConnectorListener.java new file mode 100644 index 00000000..318415cc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IConnectorListener.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
12 | |||
13 | /** | ||
14 | * Connector listeners are used to receive notifications on addition and removal of query specifications. | ||
15 | * The connector itself is also passed in the methods to allow the usage of the same listener on multiple connectors. | ||
16 | * | ||
17 | * @author Abel Hegedus | ||
18 | * @since 1.3 | ||
19 | * | ||
20 | */ | ||
21 | public interface IConnectorListener { | ||
22 | |||
23 | /** | ||
24 | * Called when a new query specification is added to the given connector. | ||
25 | * The provider interface is used to avoid class loading as long as possible. | ||
26 | * | ||
27 | * @param connector that has a new specification | ||
28 | * @param specificationProvider that wraps the new specification | ||
29 | */ | ||
30 | void querySpecificationAdded(IRegistrySourceConnector connector, IQuerySpecificationProvider specificationProvider); | ||
31 | |||
32 | /** | ||
33 | * Called when a query specification is removed from the given connector. | ||
34 | * The provider interface is used to avoid class loading as long as possible. | ||
35 | * | ||
36 | * @param connector that has a removed specification | ||
37 | * @param specificationProvider that wraps the removed specification | ||
38 | */ | ||
39 | void querySpecificationRemoved(IRegistrySourceConnector connector, IQuerySpecificationProvider specificationProvider); | ||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IDefaultRegistryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IDefaultRegistryView.java new file mode 100644 index 00000000..9e3e0394 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IDefaultRegistryView.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import java.util.NoSuchElementException; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
14 | |||
15 | /** | ||
16 | * The default registry view ensures that the fully qualified name of entries are unique and provides an additional | ||
17 | * method for retrieving the query group of entries for easy initialization. | ||
18 | * | ||
19 | * @author Abel Hegedus | ||
20 | * @since 1.3 | ||
21 | * | ||
22 | */ | ||
23 | public interface IDefaultRegistryView extends IRegistryView { | ||
24 | |||
25 | /** | ||
26 | * @return a query group containing all query specifications | ||
27 | */ | ||
28 | IQueryGroup getQueryGroup(); | ||
29 | |||
30 | /** | ||
31 | * @param fullyQualifiedName | ||
32 | * of the entry that is requested | ||
33 | * @return the entry with the given FQN | ||
34 | * @throws NoSuchElementException if there is no such entry in the default view | ||
35 | */ | ||
36 | IQuerySpecificationRegistryEntry getEntry(String fullyQualifiedName); | ||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistry.java new file mode 100644 index 00000000..97a17f0e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistry.java | |||
@@ -0,0 +1,74 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | /** | ||
12 | * The query specification registry is used to manage query specifications provided by multiple connectors which can | ||
13 | * dynamically add and remove specifications. Users can read the contents of the registry through views that are also | ||
14 | * dynamically updated when the registry is changed by the connectors. | ||
15 | * | ||
16 | * @author Abel Hegedus | ||
17 | * @since 1.3 | ||
18 | * | ||
19 | */ | ||
20 | public interface IQuerySpecificationRegistry { | ||
21 | |||
22 | /** | ||
23 | * Cannot register connectors with the same identifier twice. No change occurs if the identifier is already used. | ||
24 | * | ||
25 | * @param connector | ||
26 | * cannot be null | ||
27 | * @return false if a connector with the given identifier has already been added, true otherwise | ||
28 | */ | ||
29 | boolean addSource(IRegistrySourceConnector connector); | ||
30 | |||
31 | /** | ||
32 | * Removes the connector if it was registered. No change occurs if the identifier of the connector was not used | ||
33 | * before. | ||
34 | * | ||
35 | * @param connector | ||
36 | * cannot be null | ||
37 | * @return false if a registered connector with the given identifier was not found, true if it was successfully | ||
38 | * removed | ||
39 | */ | ||
40 | boolean removeSource(IRegistrySourceConnector connector); | ||
41 | |||
42 | /** | ||
43 | * Returns a default view instance that contains query specification entries that indicate their inclusion in | ||
44 | * default views. If there are entries with the same FQN, only the last added will be included in the view to avoid | ||
45 | * duplicate FQNs. | ||
46 | * | ||
47 | * @return the default view instance | ||
48 | */ | ||
49 | IDefaultRegistryView getDefaultView(); | ||
50 | |||
51 | /** | ||
52 | * Creates a view which contains query specification entries that indicate their inclusion in default views. This | ||
53 | * view will also be incrementally updated on registry changes and accepts listeners to notify on changes. | ||
54 | * | ||
55 | * @return a new view instance | ||
56 | */ | ||
57 | IRegistryView createView(); | ||
58 | |||
59 | /** | ||
60 | * Creates a view which contains registered query specifications that are considered relevant by the passed filter. | ||
61 | * This view will also be incrementally updated on registry changes and accepts listeners to notify on changes. | ||
62 | * | ||
63 | * @return a new filtered view instance | ||
64 | */ | ||
65 | IRegistryView createView(IRegistryViewFilter filter); | ||
66 | |||
67 | /** | ||
68 | * Creates a view which is instantiated by the factory and is connected to the registry. This | ||
69 | * view will also be incrementally updated on registry changes and accepts listeners to notify on changes. | ||
70 | * | ||
71 | * @return a new view instance | ||
72 | */ | ||
73 | IRegistryView createView(IRegistryViewFactory factory); | ||
74 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryChangeListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryChangeListener.java new file mode 100644 index 00000000..defdd2ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryChangeListener.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | /** | ||
12 | * Listener interface for providing update notifications of views to users. It is used for propagating changes from the | ||
13 | * query specification registry to the views and from the views to users. | ||
14 | * | ||
15 | * @author Abel Hegedus | ||
16 | * @since 1.3 | ||
17 | * | ||
18 | */ | ||
19 | public interface IQuerySpecificationRegistryChangeListener { | ||
20 | |||
21 | /** | ||
22 | * Called when a new entry is added to the registry. | ||
23 | * | ||
24 | * @param entry that is added | ||
25 | */ | ||
26 | void entryAdded(IQuerySpecificationRegistryEntry entry); | ||
27 | |||
28 | /** | ||
29 | * Called when an existing entry is removed from the registry. | ||
30 | * | ||
31 | * @param entry that is removed | ||
32 | */ | ||
33 | void entryRemoved(IQuerySpecificationRegistryEntry entry); | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryEntry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryEntry.java new file mode 100644 index 00000000..5009d74b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryEntry.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
12 | |||
13 | /** | ||
14 | * The query specification registry entry interface can return the identifier of the source that added it to the | ||
15 | * registry. It is provider based and can delay class loading of the wrapped {@link IQuerySpecification} until needed. | ||
16 | * | ||
17 | * @author Abel Hegedus | ||
18 | * @since 1.3 | ||
19 | * | ||
20 | */ | ||
21 | public interface IQuerySpecificationRegistryEntry extends IQuerySpecificationProvider { | ||
22 | |||
23 | /** | ||
24 | * @return the identifier of the registry source that contributed the specification | ||
25 | */ | ||
26 | String getSourceIdentifier(); | ||
27 | |||
28 | /** | ||
29 | * Returns whether the query specification was provided by an identifiable project. | ||
30 | */ | ||
31 | boolean isFromProject(); | ||
32 | |||
33 | /** | ||
34 | * Collects the name of the project that is registered this specification to the registry. | ||
35 | * If {@link #getSourceIdentifier()} is false, it returns null. | ||
36 | */ | ||
37 | String getSourceProjectName(); | ||
38 | |||
39 | /** | ||
40 | * @return true if the entry should be included in default views (created without any filters) | ||
41 | */ | ||
42 | boolean includeInDefaultViews(); | ||
43 | |||
44 | /** | ||
45 | * @return the wrapped {@link IQuerySpecificationProvider} or itself | ||
46 | */ | ||
47 | IQuerySpecificationProvider getProvider(); | ||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistrySourceConnector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistrySourceConnector.java new file mode 100644 index 00000000..94c68d1b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistrySourceConnector.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | /** | ||
12 | * A registry source connector can provide query specifications to listeners (e.g. {@link IQuerySpecificationRegistry}). | ||
13 | * The connector interface does not support direct access to query specifications, instead it sends existing specifications | ||
14 | * to listeners on addition and sends notifications to listeners when a change occurs in the set of specifications. | ||
15 | * | ||
16 | * @author Abel Hegedus | ||
17 | * @since 1.3 | ||
18 | * | ||
19 | */ | ||
20 | public interface IRegistrySourceConnector { | ||
21 | |||
22 | /** | ||
23 | * The connector must return the same identifier every time it is invoked! | ||
24 | * | ||
25 | * @return unique identifier of the connector | ||
26 | */ | ||
27 | String getIdentifier(); | ||
28 | |||
29 | /** | ||
30 | * | ||
31 | * @return true if the specifications of the connector should be included in default views | ||
32 | */ | ||
33 | boolean includeSpecificationsInDefaultViews(); | ||
34 | |||
35 | /** | ||
36 | * Add a listener to get updates on changes in the query specifications available from the connector. When the | ||
37 | * listener is added, the connector is expected to call the listener with each existing query specification. | ||
38 | * | ||
39 | * @param listener that should be added | ||
40 | */ | ||
41 | void addListener(IConnectorListener listener); | ||
42 | |||
43 | /** | ||
44 | * Removes an already registered listener and stops sending updates. The connector is not required to send any | ||
45 | * updates before returning from this method, but should not send any events after this method returns. | ||
46 | * | ||
47 | * @param listener that should be removed | ||
48 | */ | ||
49 | void removeListener(IConnectorListener listener); | ||
50 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryView.java new file mode 100644 index 00000000..acf49b76 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryView.java | |||
@@ -0,0 +1,72 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | /** | ||
14 | * The registry view is the primary interface for users to interact with the query specifications in an | ||
15 | * {@link IQuerySpecificationRegistry}. Views are created using the createView methods of registry and their content is | ||
16 | * also dynamically updated by the registry. | ||
17 | * | ||
18 | * The view contains a set of {@link IQuerySpecificationRegistryEntry} objects that can be used to access the query | ||
19 | * specifications themselves through the get() method. | ||
20 | * | ||
21 | * Users can check the contents of the view and add listeners to get notifications on view changes (added or removed | ||
22 | * entries). | ||
23 | * | ||
24 | * @author Abel Hegedus | ||
25 | * @since 1.3 | ||
26 | * | ||
27 | */ | ||
28 | public interface IRegistryView extends IQuerySpecificationRegistryChangeListener { | ||
29 | |||
30 | /** | ||
31 | * @return an immutable copy of all entries found in the view | ||
32 | */ | ||
33 | Iterable<IQuerySpecificationRegistryEntry> getEntries(); | ||
34 | |||
35 | /** | ||
36 | * @return the set of FQNs for the query specifications in the view | ||
37 | */ | ||
38 | Set<String> getQuerySpecificationFQNs(); | ||
39 | |||
40 | /** | ||
41 | * @param fullyQualifiedName | ||
42 | * that is looked up in the view | ||
43 | * @return true if the view contains an entry with given FQN, false otherwise | ||
44 | */ | ||
45 | boolean hasQuerySpecificationFQN(String fullyQualifiedName); | ||
46 | |||
47 | /** | ||
48 | * @param fullyQualifiedName | ||
49 | * of the entries that are requested | ||
50 | * @return the possible empty set of entries with the given FQN | ||
51 | */ | ||
52 | Set<IQuerySpecificationRegistryEntry> getEntries(String fullyQualifiedName); | ||
53 | |||
54 | /** | ||
55 | * Adds a listener to the view that will be notified when an entry is added to or removed from the view. | ||
56 | * | ||
57 | * @param listener that is added | ||
58 | */ | ||
59 | void addViewListener(IQuerySpecificationRegistryChangeListener listener); | ||
60 | |||
61 | /** | ||
62 | * Removes a listener that was previously added to the view. | ||
63 | * | ||
64 | * @param listener that is removed | ||
65 | */ | ||
66 | void removeViewListener(IQuerySpecificationRegistryChangeListener listener); | ||
67 | |||
68 | /** | ||
69 | * @return the registry underlying the view | ||
70 | */ | ||
71 | IQuerySpecificationRegistry getRegistry(); | ||
72 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFactory.java new file mode 100644 index 00000000..990902d3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFactory.java | |||
@@ -0,0 +1,34 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | /** | ||
12 | * This interface can be used to ask the registry to construct specific view instances. The factory is responsible for | ||
13 | * instantiating the view, but the registry is responsible for establishing the connection with the internal data store | ||
14 | * and to fill up the view with entries. Instances of the factory are intended to be passed to | ||
15 | * {@link IQuerySpecificationRegistry#createView(IRegistryViewFactory)} and only the view instance returned by that | ||
16 | * method can be considered initialized. | ||
17 | * | ||
18 | * @author Abel Hegedus | ||
19 | * @since 1.3 | ||
20 | * | ||
21 | */ | ||
22 | public interface IRegistryViewFactory { | ||
23 | |||
24 | /** | ||
25 | * Instantiate a new view object and store the reference to the registry. | ||
26 | * This method should only be called by an {@link IQuerySpecificationRegistry}. | ||
27 | * | ||
28 | * @param registry that will be connected to the view | ||
29 | * @return the new instance of the view | ||
30 | * @noreference This method is not intended to be referenced by clients. | ||
31 | */ | ||
32 | IRegistryView createView(IQuerySpecificationRegistry registry); | ||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFilter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFilter.java new file mode 100644 index 00000000..fa13327f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFilter.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | /** | ||
12 | * The registry view filter can control which entries are added and removed from an {@link IRegistryView}. | ||
13 | * | ||
14 | * @author Abel Hegedus | ||
15 | * @since 1.3 | ||
16 | * | ||
17 | */ | ||
18 | public interface IRegistryViewFilter { | ||
19 | |||
20 | /** | ||
21 | * This method controls whether a registry entry is added to the view or not. The filtering is called before | ||
22 | * checking the uniqueness of fully qualified names, and relevant entries can overwrite existing entries with the | ||
23 | * same FQN. | ||
24 | * | ||
25 | * Note that filters should usually return the same value for the same entry on multiple invocations. | ||
26 | * | ||
27 | * @param entry | ||
28 | * that is checked | ||
29 | * @return true, if the entry is relevant for the view, false otherwise | ||
30 | */ | ||
31 | boolean isEntryRelevant(IQuerySpecificationRegistryEntry entry); | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/QuerySpecificationRegistry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/QuerySpecificationRegistry.java new file mode 100644 index 00000000..139dde1c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/QuerySpecificationRegistry.java | |||
@@ -0,0 +1,112 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.registry.impl.QuerySpecificationRegistryImpl; | ||
16 | |||
17 | /** | ||
18 | * Registry for query specifications that can be accessed using fully qualified names through views. | ||
19 | * Additional query specifications can be added using {@link IRegistrySourceConnector}s. | ||
20 | * | ||
21 | * <p> | ||
22 | * When running as an OSGi plug-in, the generated query specifications registered through extensions are automatically loaded | ||
23 | * into the registry by the {@link ExtensionBasedQuerySpecificationLoader} class. | ||
24 | * | ||
25 | * @author Abel Hegedus | ||
26 | * @since 1.3 | ||
27 | * | ||
28 | */ | ||
29 | public class QuerySpecificationRegistry implements IQuerySpecificationRegistry { | ||
30 | |||
31 | private static final QuerySpecificationRegistry INSTANCE = new QuerySpecificationRegistry(); | ||
32 | |||
33 | /** | ||
34 | * @return the singleton query specification registry instance | ||
35 | */ | ||
36 | public static IQuerySpecificationRegistry getInstance() { | ||
37 | return INSTANCE; | ||
38 | } | ||
39 | |||
40 | private final QuerySpecificationRegistryImpl internalRegistry; | ||
41 | /** | ||
42 | * Connectors that should not be immediately loaded into the registry. | ||
43 | */ | ||
44 | private final Set<IRegistrySourceConnector> delayedConnectors; | ||
45 | |||
46 | /** | ||
47 | * Hidden constructor for singleton instance | ||
48 | */ | ||
49 | protected QuerySpecificationRegistry() { | ||
50 | this.internalRegistry = new QuerySpecificationRegistryImpl(); | ||
51 | this.delayedConnectors = new HashSet<>(); | ||
52 | |||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @return the internal registry after adding delayed source connectors | ||
57 | */ | ||
58 | protected IQuerySpecificationRegistry getInternalRegistry() { | ||
59 | if(!delayedConnectors.isEmpty()) { | ||
60 | final Iterator<IRegistrySourceConnector> it = delayedConnectors.iterator(); | ||
61 | while (it.hasNext()) { | ||
62 | final IRegistrySourceConnector connector = it.next(); | ||
63 | internalRegistry.addSource(connector); | ||
64 | it.remove(); | ||
65 | } | ||
66 | } | ||
67 | return internalRegistry; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * When the registry adds itself as a listener to connectors, it must send all specification providers | ||
72 | * to the registry. However, when {@link ExtensionBasedQuerySpecificationLoader} is triggered during the | ||
73 | * activation of the plugin, the individual query specification classes cannot be loaded yet. To avoid this, | ||
74 | * the connector of the loader is delayed until needed. | ||
75 | * | ||
76 | * @param connector that should be delayed before adding to the registry | ||
77 | */ | ||
78 | protected void addDelayedSourceConnector(IRegistrySourceConnector connector) { | ||
79 | delayedConnectors.add(connector); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean addSource(IRegistrySourceConnector connector) { | ||
84 | return getInternalRegistry().addSource(connector); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public boolean removeSource(IRegistrySourceConnector connector) { | ||
89 | return getInternalRegistry().removeSource(connector); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public IDefaultRegistryView getDefaultView() { | ||
94 | return getInternalRegistry().getDefaultView(); | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public IRegistryView createView() { | ||
99 | return getInternalRegistry().createView(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public IRegistryView createView(IRegistryViewFilter filter) { | ||
104 | return getInternalRegistry().createView(filter); | ||
105 | } | ||
106 | |||
107 | @Override | ||
108 | public IRegistryView createView(IRegistryViewFactory factory) { | ||
109 | return getInternalRegistry().createView(factory); | ||
110 | } | ||
111 | |||
112 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/AbstractRegistrySourceConnector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/AbstractRegistrySourceConnector.java new file mode 100644 index 00000000..0e2ef273 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/AbstractRegistrySourceConnector.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.connector; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Objects; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.registry.IConnectorListener; | ||
16 | import tools.refinery.viatra.runtime.registry.IRegistrySourceConnector; | ||
17 | |||
18 | /** | ||
19 | * Abstract registry source connector implementation that stores the identifier and listener set. | ||
20 | * | ||
21 | * | ||
22 | * @author Abel Hegedus | ||
23 | * @since 1.3 | ||
24 | * | ||
25 | */ | ||
26 | public abstract class AbstractRegistrySourceConnector implements IRegistrySourceConnector { | ||
27 | |||
28 | protected Set<IConnectorListener> listeners; | ||
29 | private String identifier; | ||
30 | private boolean includeInDefaultViews; | ||
31 | |||
32 | /** | ||
33 | * Creates an instance of the connector with the given identifier. The identifier should be unique if you want to | ||
34 | * add it to a registry as a source. | ||
35 | * | ||
36 | * @param identifier | ||
37 | * of the newly created connector | ||
38 | * @param includeInDefaultViews | ||
39 | * true if the specifications in the connector should be included in default views | ||
40 | */ | ||
41 | public AbstractRegistrySourceConnector(String identifier, boolean includeInDefaultViews) { | ||
42 | super(); | ||
43 | Objects.requireNonNull(identifier, "Identifier must not be null!"); | ||
44 | this.identifier = identifier; | ||
45 | this.includeInDefaultViews = includeInDefaultViews; | ||
46 | this.listeners = new HashSet<>(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public String getIdentifier() { | ||
51 | return identifier; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public boolean includeSpecificationsInDefaultViews() { | ||
56 | return includeInDefaultViews; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public void addListener(IConnectorListener listener) { | ||
61 | Objects.requireNonNull(listener, "Listener must not be null!"); | ||
62 | boolean added = listeners.add(listener); | ||
63 | if (added) { | ||
64 | sendQuerySpecificationsToListener(listener); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public void removeListener(IConnectorListener listener) { | ||
70 | Objects.requireNonNull(listener, "Listener must not be null!"); | ||
71 | listeners.remove(listener); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * Subclasses should send add notifications for each specification in the connector to the given listener. | ||
76 | * | ||
77 | * @param listener that should receive the notifications | ||
78 | */ | ||
79 | protected abstract void sendQuerySpecificationsToListener(IConnectorListener listener); | ||
80 | |||
81 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/QueryGroupProviderSourceConnector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/QueryGroupProviderSourceConnector.java new file mode 100644 index 00000000..e6f0adf0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/QueryGroupProviderSourceConnector.java | |||
@@ -0,0 +1,80 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.connector; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.extensibility.IQueryGroupProvider; | ||
14 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
15 | import tools.refinery.viatra.runtime.registry.IConnectorListener; | ||
16 | |||
17 | /** | ||
18 | * Source connector implementation that uses a {@link IQueryGroupProvider} to provide a query specifications into the | ||
19 | * registry. The query group can be later updated which triggers the removal of all specifications of the old group and | ||
20 | * the addition of all specifications from the new group. | ||
21 | * | ||
22 | * @author Abel Hegedus | ||
23 | * @since 1.3 | ||
24 | * | ||
25 | */ | ||
26 | public class QueryGroupProviderSourceConnector extends AbstractRegistrySourceConnector { | ||
27 | |||
28 | IQueryGroupProvider queryGroupProvider; | ||
29 | |||
30 | /** | ||
31 | * Creates an instance of the connector with the given identifier and the query group provider. The identifier | ||
32 | * should be unique if you want to add it to a registry as a source. | ||
33 | * | ||
34 | * @param identifier | ||
35 | * of the newly created connector | ||
36 | * @param provider | ||
37 | * that contains the query specifications handled by the connector | ||
38 | * @param includeInDefaultViews | ||
39 | * true if the specifications in the connector should be included in default views | ||
40 | */ | ||
41 | public QueryGroupProviderSourceConnector(String identifier, IQueryGroupProvider provider, boolean includeInDefaultViews) { | ||
42 | super(identifier, includeInDefaultViews); | ||
43 | this.queryGroupProvider = provider; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * Update the query group of the connector, which triggers the removal of all specifications on the old group and | ||
48 | * addition of all specifications in the given group. | ||
49 | * | ||
50 | * @param queryGroupProvider | ||
51 | * the queryGroupProvider to set | ||
52 | * @param includeInDefaultViews | ||
53 | * true if the specifications in the connector should be included in default views | ||
54 | */ | ||
55 | public void setQueryGroupProvider(IQueryGroupProvider queryGroupProvider) { | ||
56 | Objects.requireNonNull(queryGroupProvider, "Query group provider must not be null!"); | ||
57 | IQueryGroupProvider oldProvider = this.queryGroupProvider; | ||
58 | |||
59 | for (IQuerySpecificationProvider specificationProvider : oldProvider.getQuerySpecificationProviders()) { | ||
60 | for (IConnectorListener iConnectorListener : listeners) { | ||
61 | iConnectorListener.querySpecificationRemoved(this, specificationProvider); | ||
62 | } | ||
63 | } | ||
64 | for (IQuerySpecificationProvider specificationProvider : queryGroupProvider.getQuerySpecificationProviders()) { | ||
65 | for (IConnectorListener iConnectorListener : listeners) { | ||
66 | iConnectorListener.querySpecificationAdded(this, specificationProvider); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | this.queryGroupProvider = queryGroupProvider; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | protected void sendQuerySpecificationsToListener(IConnectorListener listener) { | ||
75 | for (IQuerySpecificationProvider specificationProvider : queryGroupProvider.getQuerySpecificationProviders()) { | ||
76 | listener.querySpecificationAdded(this, specificationProvider); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/SpecificationMapSourceConnector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/SpecificationMapSourceConnector.java new file mode 100644 index 00000000..e5778950 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/SpecificationMapSourceConnector.java | |||
@@ -0,0 +1,147 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.connector; | ||
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.Objects; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
20 | import tools.refinery.viatra.runtime.extensibility.SingletonQuerySpecificationProvider; | ||
21 | import tools.refinery.viatra.runtime.registry.IConnectorListener; | ||
22 | |||
23 | /** | ||
24 | * A simple connector implementation that allows users to simply add and remove specifications. These changes are | ||
25 | * propagated to listeners (e.g. the registry). Note that duplicate FQNs are not allowed in a given connector. | ||
26 | * | ||
27 | * @author Abel Hegedus | ||
28 | * @since 1.3 | ||
29 | * | ||
30 | */ | ||
31 | public class SpecificationMapSourceConnector extends AbstractRegistrySourceConnector { | ||
32 | |||
33 | private static final String DUPLICATE_MESSAGE = "Duplicate FQN %s cannot be added to connector"; | ||
34 | private Map<String, IQuerySpecificationProvider> specificationProviderMap; | ||
35 | |||
36 | /** | ||
37 | * Creates an instance of the connector with the given identifier. The identifier should be unique if you want to | ||
38 | * add it to a registry as a source. | ||
39 | * | ||
40 | * @param identifier | ||
41 | * of the newly created connector | ||
42 | * @param includeInDefaultViews | ||
43 | * true if the specifications in the connector should be included in default views | ||
44 | */ | ||
45 | public SpecificationMapSourceConnector(String identifier, boolean includeInDefaultViews) { | ||
46 | super(identifier, includeInDefaultViews); | ||
47 | this.specificationProviderMap = new HashMap<>(); | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Creates an instance of the connector with the given identifier and fills it up with the given specification | ||
52 | * providers. The identifier should be unique if you want to add it to a registry as a source. | ||
53 | * | ||
54 | * @param identifier | ||
55 | * of the newly created connector | ||
56 | * @param specificationProviders | ||
57 | * the initial set of specifications in the connector | ||
58 | * @param includeInDefaultViews | ||
59 | * true if the specifications in the connector should be included in default views | ||
60 | */ | ||
61 | public SpecificationMapSourceConnector(String identifier, Set<IQuerySpecificationProvider> specificationProviders, boolean includeInDefaultViews) { | ||
62 | this(identifier, includeInDefaultViews); | ||
63 | for (IQuerySpecificationProvider provider : specificationProviders) { | ||
64 | addQuerySpecificationProvider(provider); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Creates an instance of the connector with the given identifier and fills it up with the specification providers | ||
70 | * from the given {@link SpecificationMapSourceConnector}. The identifier should be unique if you want to add it to | ||
71 | * a registry as a source. | ||
72 | * | ||
73 | * @param identifier | ||
74 | * of the newly created connector | ||
75 | * @param connector | ||
76 | * that contains the specifications to copy into the new instance | ||
77 | * @param includeInDefaultViews | ||
78 | * true if the specifications in the connector should be included in default views | ||
79 | */ | ||
80 | public SpecificationMapSourceConnector(String identifier, SpecificationMapSourceConnector connector, boolean includeInDefaultViews) { | ||
81 | this(identifier, includeInDefaultViews); | ||
82 | this.specificationProviderMap.putAll(connector.specificationProviderMap); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Adds a query specification to the connector. | ||
87 | * If you have an {@link IQuerySpecification} object, use {@link SingletonQuerySpecificationProvider}. | ||
88 | * | ||
89 | * @param provider to add to the connector | ||
90 | * @throws IllegalArgumentException if the connector already contains a specification with the same FQN | ||
91 | */ | ||
92 | public void addQuerySpecificationProvider(IQuerySpecificationProvider provider) { | ||
93 | Objects.requireNonNull(provider, "Provider must not be null!"); | ||
94 | String fullyQualifiedName = provider.getFullyQualifiedName(); | ||
95 | if (!specificationProviderMap.containsKey(fullyQualifiedName)) { | ||
96 | specificationProviderMap.put(fullyQualifiedName, provider); | ||
97 | for (IConnectorListener listener : listeners) { | ||
98 | listener.querySpecificationAdded(this, provider); | ||
99 | } | ||
100 | } else { | ||
101 | throw new IllegalArgumentException(String.format(DUPLICATE_MESSAGE, fullyQualifiedName)); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Remove a specification that has been added with the given FQN. | ||
107 | * | ||
108 | * @param fullyQualifiedName | ||
109 | * @throws NoSuchElementException if the connector does not contain a specification with the given FQN | ||
110 | */ | ||
111 | public void removeQuerySpecificationProvider(String fullyQualifiedName) { | ||
112 | Objects.requireNonNull(fullyQualifiedName, "Fully qualified name must not be null!"); | ||
113 | IQuerySpecificationProvider provider = specificationProviderMap.remove(fullyQualifiedName); | ||
114 | if (provider == null) { | ||
115 | throw new NoSuchElementException( | ||
116 | String.format("Connector does not contain specification with FQN %s", fullyQualifiedName)); | ||
117 | } | ||
118 | for (IConnectorListener listener : listeners) { | ||
119 | listener.querySpecificationRemoved(this, provider); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @return the immutable copy of the set of FQNs for the added query specifications | ||
125 | */ | ||
126 | public Set<String> getQuerySpecificationFQNs() { | ||
127 | return Collections.unmodifiableSet(new HashSet<>(specificationProviderMap.keySet())); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * | ||
132 | * @param fullyQualifiedName that is checked | ||
133 | * @return true if a specification with the given FQN exists in the connector, false otherwise | ||
134 | */ | ||
135 | public boolean hasQuerySpecificationFQN(String fullyQualifiedName) { | ||
136 | Objects.requireNonNull(fullyQualifiedName, "FQN must not be null!"); | ||
137 | return specificationProviderMap.containsKey(fullyQualifiedName); | ||
138 | } | ||
139 | |||
140 | @Override | ||
141 | protected void sendQuerySpecificationsToListener(IConnectorListener listener) { | ||
142 | for (IQuerySpecificationProvider provider : specificationProviderMap.values()) { | ||
143 | listener.querySpecificationAdded(this, provider); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/QuerySpecificationStore.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/QuerySpecificationStore.java new file mode 100644 index 00000000..649527b6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/QuerySpecificationStore.java | |||
@@ -0,0 +1,38 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.data; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.TreeMap; | ||
13 | |||
14 | /** | ||
15 | * Internal data storage object that represents a query specification registry with a set of sources driven by | ||
16 | * connectors. The sources must have unique identifiers. | ||
17 | * | ||
18 | * @author Abel Hegedus | ||
19 | * | ||
20 | */ | ||
21 | public class QuerySpecificationStore { | ||
22 | |||
23 | private Map<String, RegistrySourceImpl> sources; | ||
24 | |||
25 | /** | ||
26 | * Creates a new instance with an empty identifier to source map. | ||
27 | */ | ||
28 | public QuerySpecificationStore() { | ||
29 | this.sources = new TreeMap<>(); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * @return the live, modifiable identifier to source map | ||
34 | */ | ||
35 | public Map<String, RegistrySourceImpl> getSources() { | ||
36 | return sources; | ||
37 | } | ||
38 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistryEntryImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistryEntryImpl.java new file mode 100644 index 00000000..758885e2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistryEntryImpl.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.data; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.IQuerySpecification; | ||
12 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
13 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
14 | |||
15 | /** | ||
16 | * Internal data storage object that represents a query specification entry. The entry contains an | ||
17 | * {@link IQuerySpecificationProvider} and has a reference to the source that contains it. | ||
18 | * | ||
19 | * @author Abel Hegedus | ||
20 | * | ||
21 | */ | ||
22 | public class RegistryEntryImpl implements IQuerySpecificationRegistryEntry { | ||
23 | |||
24 | private IQuerySpecificationProvider provider; | ||
25 | private RegistrySourceImpl source; | ||
26 | |||
27 | /** | ||
28 | * Creates a new instance with the given source and the given provider. | ||
29 | * | ||
30 | * @param source | ||
31 | * that contains the new entry | ||
32 | * @param provider | ||
33 | * that wraps the specification represented by the entry | ||
34 | */ | ||
35 | public RegistryEntryImpl(RegistrySourceImpl source, IQuerySpecificationProvider provider) { | ||
36 | this.source = source; | ||
37 | this.provider = provider; | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @return the source that contains this entry | ||
42 | */ | ||
43 | public RegistrySourceImpl getSource() { | ||
44 | return source; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String getSourceIdentifier() { | ||
49 | return source.getIdentifier(); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean includeInDefaultViews() { | ||
54 | return getSource().includeEntriesInDefaultViews(); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public String getFullyQualifiedName() { | ||
59 | return provider.getFullyQualifiedName(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public IQuerySpecification<?> get() { | ||
64 | return provider.get(); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public IQuerySpecificationProvider getProvider() { | ||
69 | return provider; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean isFromProject() { | ||
74 | return provider.getSourceProjectName() != null; | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public String getSourceProjectName() { | ||
79 | return provider.getSourceProjectName(); | ||
80 | } | ||
81 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistrySourceImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistrySourceImpl.java new file mode 100644 index 00000000..83c3b920 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistrySourceImpl.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.data; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.TreeMap; | ||
13 | |||
14 | /** | ||
15 | * Internal data storage object that represents a query specification source driven by a connector. The source must have | ||
16 | * unique identifier that is copied from the connector. The source uses a FQN to entry map to manage registry entries. | ||
17 | * | ||
18 | * @author Abel Hegedus | ||
19 | * | ||
20 | */ | ||
21 | public class RegistrySourceImpl { | ||
22 | |||
23 | private String identifier; | ||
24 | private boolean includeInDefaultViews; | ||
25 | private QuerySpecificationStore querySpecificationStore; | ||
26 | private Map<String, RegistryEntryImpl> fqnToEntryMap; | ||
27 | |||
28 | /** | ||
29 | * Creates a new source with the given identifier and an empty entry map. | ||
30 | * | ||
31 | * @param identifier | ||
32 | * for the source | ||
33 | * @param querySpecificationStore | ||
34 | * that contains this source | ||
35 | * @param includeInDefaultViews | ||
36 | * true if the entries of the source should be included in default views | ||
37 | */ | ||
38 | public RegistrySourceImpl(String identifier, QuerySpecificationStore querySpecificationStore, boolean includeInDefaultViews) { | ||
39 | this.identifier = identifier; | ||
40 | this.includeInDefaultViews = includeInDefaultViews; | ||
41 | this.querySpecificationStore = querySpecificationStore; | ||
42 | this.fqnToEntryMap = new TreeMap<>(); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * @return the identifier of the source | ||
47 | */ | ||
48 | public String getIdentifier() { | ||
49 | return identifier; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * @return true if the entries in the source should be included in default views | ||
54 | */ | ||
55 | public boolean includeEntriesInDefaultViews() { | ||
56 | return includeInDefaultViews; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @return the store that contains the source | ||
61 | */ | ||
62 | public QuerySpecificationStore getStore() { | ||
63 | return querySpecificationStore; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @return the live, modifiable FQN to entry map | ||
68 | */ | ||
69 | public Map<String, RegistryEntryImpl> getFqnToEntryMap() { | ||
70 | return fqnToEntryMap; | ||
71 | } | ||
72 | |||
73 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/FilteringRegistryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/FilteringRegistryView.java new file mode 100644 index 00000000..164a30d3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/FilteringRegistryView.java | |||
@@ -0,0 +1,43 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.impl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
12 | import tools.refinery.viatra.runtime.registry.IRegistryViewFilter; | ||
13 | import tools.refinery.viatra.runtime.registry.view.AbstractRegistryView; | ||
14 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
15 | |||
16 | /** | ||
17 | * Registry view implementation that uses a filter to delegate the decision on whether | ||
18 | * a given specification is relevant to the view. | ||
19 | * | ||
20 | * @author Abel Hegedus | ||
21 | * | ||
22 | */ | ||
23 | public class FilteringRegistryView extends AbstractRegistryView { | ||
24 | |||
25 | private IRegistryViewFilter filter; | ||
26 | |||
27 | /** | ||
28 | * Creates a new filtering view instance. | ||
29 | * | ||
30 | * @param registry that defines the view | ||
31 | * @param filter that is used for deciding relevancy | ||
32 | */ | ||
33 | public FilteringRegistryView(IQuerySpecificationRegistry registry, IRegistryViewFilter filter, boolean allowDuplicateFQNs) { | ||
34 | super(registry, allowDuplicateFQNs); | ||
35 | this.filter = filter; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | protected boolean isEntryRelevant(IQuerySpecificationRegistryEntry entry) { | ||
40 | return filter.isEntryRelevant(entry); | ||
41 | } | ||
42 | |||
43 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/GlobalRegistryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/GlobalRegistryView.java new file mode 100644 index 00000000..75d730b6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/GlobalRegistryView.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.impl; | ||
10 | |||
11 | import java.util.NoSuchElementException; | ||
12 | import java.util.Set; | ||
13 | import java.util.stream.Collectors; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.api.IQueryGroup; | ||
16 | import tools.refinery.viatra.runtime.api.LazyLoadingQueryGroup; | ||
17 | import tools.refinery.viatra.runtime.registry.IDefaultRegistryView; | ||
18 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
19 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
20 | import tools.refinery.viatra.runtime.registry.view.AbstractRegistryView; | ||
21 | |||
22 | /** | ||
23 | * Registry view implementation that considers specifications relevant if they are included in default views. | ||
24 | * | ||
25 | * @author Abel Hegedus | ||
26 | * | ||
27 | */ | ||
28 | public class GlobalRegistryView extends AbstractRegistryView implements IDefaultRegistryView { | ||
29 | |||
30 | /** | ||
31 | * Creates a new instance of the global view. | ||
32 | * | ||
33 | * @param registry that defines the view | ||
34 | */ | ||
35 | public GlobalRegistryView(IQuerySpecificationRegistry registry) { | ||
36 | super(registry, false); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected boolean isEntryRelevant(IQuerySpecificationRegistryEntry entry) { | ||
41 | return entry.includeInDefaultViews(); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public IQueryGroup getQueryGroup() { | ||
46 | Set<IQuerySpecificationRegistryEntry> allQueries = | ||
47 | fqnToEntryMap.distinctValuesStream().collect(Collectors.toSet()); | ||
48 | IQueryGroup queryGroup = LazyLoadingQueryGroup.of(allQueries); | ||
49 | return queryGroup; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public IQuerySpecificationRegistryEntry getEntry(String fullyQualifiedName) { | ||
54 | Set<IQuerySpecificationRegistryEntry> entries = getEntries(fullyQualifiedName); | ||
55 | if(entries.isEmpty()){ | ||
56 | throw new NoSuchElementException("Cannot find entry with FQN " + fullyQualifiedName); | ||
57 | } | ||
58 | if(entries.size() > 1) { | ||
59 | throw new IllegalStateException("Global view must never contain duplicated FQNs!"); | ||
60 | } | ||
61 | IQuerySpecificationRegistryEntry entry = entries.iterator().next(); | ||
62 | return entry; | ||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/QuerySpecificationRegistryImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/QuerySpecificationRegistryImpl.java new file mode 100644 index 00000000..63306e93 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/QuerySpecificationRegistryImpl.java | |||
@@ -0,0 +1,177 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.impl; | ||
10 | |||
11 | import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument; | ||
12 | |||
13 | import java.util.Map; | ||
14 | |||
15 | import org.apache.log4j.Logger; | ||
16 | import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider; | ||
17 | import tools.refinery.viatra.runtime.registry.IConnectorListener; | ||
18 | import tools.refinery.viatra.runtime.registry.IDefaultRegistryView; | ||
19 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
20 | import tools.refinery.viatra.runtime.registry.IRegistryView; | ||
21 | import tools.refinery.viatra.runtime.registry.IRegistryViewFactory; | ||
22 | import tools.refinery.viatra.runtime.registry.IRegistryViewFilter; | ||
23 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener; | ||
24 | import tools.refinery.viatra.runtime.registry.IRegistrySourceConnector; | ||
25 | import tools.refinery.viatra.runtime.registry.data.QuerySpecificationStore; | ||
26 | import tools.refinery.viatra.runtime.registry.data.RegistryEntryImpl; | ||
27 | import tools.refinery.viatra.runtime.registry.data.RegistrySourceImpl; | ||
28 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
29 | |||
30 | /** | ||
31 | * This is the default implementation of the {@link IQuerySpecificationRegistry} interface. | ||
32 | * It uses a {@link QuerySpecificationStore} to keep track of sources and entries. | ||
33 | * It uses a {@link RegistryChangeMultiplexer} to update all views and a single {@link IConnectorListener} | ||
34 | * to subscribe to sources added to the registry. | ||
35 | * | ||
36 | * @author Abel Hegedus | ||
37 | * | ||
38 | */ | ||
39 | public class QuerySpecificationRegistryImpl implements IQuerySpecificationRegistry { | ||
40 | |||
41 | private static final String CONNECTOR_NULL_MSG = "Connector cannot be null"; | ||
42 | private final QuerySpecificationStore querySpecificationStore; | ||
43 | private final IConnectorListener connectorListener; | ||
44 | private final RegistryChangeMultiplexer multiplexer; | ||
45 | private final Logger logger; | ||
46 | private IDefaultRegistryView defaultView = null; | ||
47 | |||
48 | /** | ||
49 | * Creates a new instance of the registry | ||
50 | */ | ||
51 | public QuerySpecificationRegistryImpl() { | ||
52 | this.querySpecificationStore = new QuerySpecificationStore(); | ||
53 | this.connectorListener = new RegistryUpdaterConnectorListener(); | ||
54 | this.multiplexer = new RegistryChangeMultiplexer(); | ||
55 | this.logger = ViatraQueryLoggingUtil.getLogger(IQuerySpecificationRegistry.class); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public boolean addSource(IRegistrySourceConnector connector) { | ||
60 | checkArgument(connector != null, CONNECTOR_NULL_MSG); | ||
61 | String identifier = connector.getIdentifier(); | ||
62 | Map<String, RegistrySourceImpl> sources = querySpecificationStore.getSources(); | ||
63 | if(sources.containsKey(identifier)){ | ||
64 | return false; | ||
65 | } | ||
66 | RegistrySourceImpl source = new RegistrySourceImpl(identifier, querySpecificationStore, connector.includeSpecificationsInDefaultViews()); | ||
67 | sources.put(identifier, source); | ||
68 | connector.addListener(connectorListener); | ||
69 | logger.debug("Source added: " + source.getIdentifier()); | ||
70 | return true; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public boolean removeSource(IRegistrySourceConnector connector) { | ||
75 | checkArgument(connector != null, CONNECTOR_NULL_MSG); | ||
76 | String identifier = connector.getIdentifier(); | ||
77 | Map<String, RegistrySourceImpl> sources = querySpecificationStore.getSources(); | ||
78 | if(!sources.containsKey(identifier)){ | ||
79 | return false; | ||
80 | } | ||
81 | connector.removeListener(connectorListener); | ||
82 | RegistrySourceImpl source = sources.remove(identifier); | ||
83 | for (RegistryEntryImpl entry : source.getFqnToEntryMap().values()) { | ||
84 | multiplexer.entryRemoved(entry); | ||
85 | } | ||
86 | logger.debug("Source removed: " + source.getIdentifier()); | ||
87 | return true; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * @return the internal store of the registry | ||
92 | */ | ||
93 | protected QuerySpecificationStore getStore() { | ||
94 | return querySpecificationStore; | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public IRegistryView createView() { | ||
99 | return createGlobalView(); | ||
100 | } | ||
101 | |||
102 | private GlobalRegistryView createGlobalView() { | ||
103 | GlobalRegistryView registryView = new GlobalRegistryView(this); | ||
104 | initializeChangeListener(registryView); | ||
105 | return registryView; | ||
106 | } | ||
107 | |||
108 | protected void initializeChangeListener(IQuerySpecificationRegistryChangeListener listener) { | ||
109 | // send existing entries to aspect | ||
110 | for (RegistrySourceImpl source : querySpecificationStore.getSources().values()) { | ||
111 | Map<String, RegistryEntryImpl> entryMap = source.getFqnToEntryMap(); | ||
112 | for (RegistryEntryImpl entry : entryMap.values()) { | ||
113 | listener.entryAdded(entry); | ||
114 | } | ||
115 | } | ||
116 | multiplexer.addListener(listener); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public IRegistryView createView(IRegistryViewFilter filter) { | ||
121 | checkArgument(filter != null, "Filter cannot be null"); | ||
122 | FilteringRegistryView registryView = new FilteringRegistryView(this, filter, false); | ||
123 | initializeChangeListener(registryView); | ||
124 | return registryView; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * Internal connector listener implementation for updating internal store and propagating changes to views. | ||
129 | * | ||
130 | * @author Abel Hegedus | ||
131 | * | ||
132 | */ | ||
133 | private final class RegistryUpdaterConnectorListener implements IConnectorListener { | ||
134 | @Override | ||
135 | public void querySpecificationAdded(IRegistrySourceConnector connector, IQuerySpecificationProvider specification) { | ||
136 | String identifier = connector.getIdentifier(); | ||
137 | RegistrySourceImpl source = querySpecificationStore.getSources().get(identifier); | ||
138 | String fullyQualifiedName = specification.getFullyQualifiedName(); | ||
139 | RegistryEntryImpl registryEntry = new RegistryEntryImpl(source, specification); | ||
140 | RegistryEntryImpl oldEntry = source.getFqnToEntryMap().put(fullyQualifiedName, registryEntry); | ||
141 | if(oldEntry != null) { | ||
142 | logger.warn(String.format("Specification added with existing FQN %s in source %s", fullyQualifiedName, identifier)); | ||
143 | multiplexer.entryRemoved(oldEntry); | ||
144 | } | ||
145 | multiplexer.entryAdded(registryEntry); | ||
146 | |||
147 | } | ||
148 | |||
149 | @Override | ||
150 | public void querySpecificationRemoved(IRegistrySourceConnector connector, IQuerySpecificationProvider specification) { | ||
151 | String identifier = connector.getIdentifier(); | ||
152 | RegistrySourceImpl source = querySpecificationStore.getSources().get(identifier); | ||
153 | String fullyQualifiedName = specification.getFullyQualifiedName(); | ||
154 | RegistryEntryImpl registryEntry = source.getFqnToEntryMap().remove(fullyQualifiedName); | ||
155 | if(registryEntry != null) { | ||
156 | multiplexer.entryRemoved(registryEntry); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public IDefaultRegistryView getDefaultView() { | ||
163 | if(this.defaultView == null){ | ||
164 | this.defaultView = createGlobalView(); | ||
165 | } | ||
166 | return this.defaultView; | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | public IRegistryView createView(IRegistryViewFactory factory) { | ||
171 | IRegistryView registryView = factory.createView(this); | ||
172 | initializeChangeListener(registryView); | ||
173 | return registryView; | ||
174 | } | ||
175 | |||
176 | |||
177 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/RegistryChangeMultiplexer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/RegistryChangeMultiplexer.java new file mode 100644 index 00000000..450f36b8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/RegistryChangeMultiplexer.java | |||
@@ -0,0 +1,58 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.impl; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | import java.util.WeakHashMap; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener; | ||
16 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
17 | |||
18 | /** | ||
19 | * Listener implementation that propagates all changes to a set of listeners. | ||
20 | * The listeners are stored with weak references to avoid a need for disposal. | ||
21 | * | ||
22 | * @author Abel Hegedus | ||
23 | * | ||
24 | */ | ||
25 | public class RegistryChangeMultiplexer implements IQuerySpecificationRegistryChangeListener { | ||
26 | |||
27 | private Set<IQuerySpecificationRegistryChangeListener> listeners; | ||
28 | |||
29 | /** | ||
30 | * Creates a new instance of the multiplexer. | ||
31 | */ | ||
32 | public RegistryChangeMultiplexer() { | ||
33 | this.listeners = Collections.newSetFromMap(new WeakHashMap<IQuerySpecificationRegistryChangeListener, Boolean>()); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Adds a weak reference on the listener to the multiplexer. The listener will receive all further notifications and | ||
38 | * does not have to be removed, since the multiplexer will not keep it in memory when it can be collected. | ||
39 | */ | ||
40 | public boolean addListener(IQuerySpecificationRegistryChangeListener listener) { | ||
41 | return listeners.add(listener); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public void entryAdded(IQuerySpecificationRegistryEntry entry) { | ||
46 | for (IQuerySpecificationRegistryChangeListener listener : listeners) { | ||
47 | listener.entryAdded(entry); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void entryRemoved(IQuerySpecificationRegistryEntry entry) { | ||
53 | for (IQuerySpecificationRegistryChangeListener listener : listeners) { | ||
54 | listener.entryRemoved(entry); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/view/AbstractRegistryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/view/AbstractRegistryView.java new file mode 100644 index 00000000..7b3c34ba --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/view/AbstractRegistryView.java | |||
@@ -0,0 +1,150 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus, 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.registry.view; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import org.apache.log4j.Logger; | ||
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.IMemoryView; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
22 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry; | ||
23 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener; | ||
24 | import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry; | ||
25 | import tools.refinery.viatra.runtime.registry.IRegistryView; | ||
26 | import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil; | ||
27 | |||
28 | /** | ||
29 | * An abstract {@link IRegistryView} implementation that stores the registry, the set of listeners added to the view and | ||
30 | * the FQN to entry map of the view itself. The only responsibility of subclasses is to decide whether an entry received | ||
31 | * as an addition or removal notification is relevant to the view. | ||
32 | * | ||
33 | * @author Abel Hegedus | ||
34 | * @since 1.3 | ||
35 | */ | ||
36 | public abstract class AbstractRegistryView implements IRegistryView { | ||
37 | |||
38 | private static final String LISTENER_EXCEPTION_REMOVE = "Exception occurred while notifying view listener %s about entry removal"; | ||
39 | private static final String LISTENER_EXCEPTION_ADD = "Exception occurred while notifying view listener %s about entry addition"; | ||
40 | protected final IQuerySpecificationRegistry registry; | ||
41 | protected final IMultiLookup<String, IQuerySpecificationRegistryEntry> fqnToEntryMap; | ||
42 | protected final Set<IQuerySpecificationRegistryChangeListener> listeners; | ||
43 | protected final boolean allowDuplicateFQNs; | ||
44 | |||
45 | /** | ||
46 | * This method is called both when an addition or removal notification is received from the registry. Subclasses can | ||
47 | * implement view filtering by returning false for those specifications that are not relevant for this view. | ||
48 | * | ||
49 | * @param entry | ||
50 | * that is added or removed in the registry | ||
51 | * @return true if the entry should be added to or removed from the view, false otherwise | ||
52 | */ | ||
53 | protected abstract boolean isEntryRelevant(IQuerySpecificationRegistryEntry entry); | ||
54 | |||
55 | /** | ||
56 | * Creates a new view instance for the given registry. Note that views are created by the registry and the view | ||
57 | * update mechanisms are also set up by the registry. | ||
58 | * | ||
59 | * @param registry | ||
60 | */ | ||
61 | public AbstractRegistryView(IQuerySpecificationRegistry registry, boolean allowDuplicateFQNs) { | ||
62 | this.registry = registry; | ||
63 | this.allowDuplicateFQNs = allowDuplicateFQNs; | ||
64 | this.fqnToEntryMap = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
65 | this.listeners = new HashSet<>(); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public IQuerySpecificationRegistry getRegistry() { | ||
70 | return registry; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public Iterable<IQuerySpecificationRegistryEntry> getEntries() { | ||
75 | return fqnToEntryMap.distinctValuesStream().collect(Collectors.toSet()); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public Set<String> getQuerySpecificationFQNs() { | ||
80 | return fqnToEntryMap.distinctKeysStream().collect(Collectors.toSet()); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public boolean hasQuerySpecificationFQN(String fullyQualifiedName) { | ||
85 | Preconditions.checkArgument(fullyQualifiedName != null, "FQN must not be null!"); | ||
86 | return fqnToEntryMap.lookupExists(fullyQualifiedName); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Set<IQuerySpecificationRegistryEntry> getEntries(String fullyQualifiedName) { | ||
91 | Preconditions.checkArgument(fullyQualifiedName != null, "FQN must not be null!"); | ||
92 | IMemoryView<IQuerySpecificationRegistryEntry> entries = fqnToEntryMap.lookupOrEmpty(fullyQualifiedName); | ||
93 | return Collections.unmodifiableSet(entries.distinctValues()); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void addViewListener(IQuerySpecificationRegistryChangeListener listener) { | ||
98 | Preconditions.checkArgument(listener != null, "Null listener not supported"); | ||
99 | listeners.add(listener); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void removeViewListener(IQuerySpecificationRegistryChangeListener listener) { | ||
104 | Preconditions.checkArgument(listener != null, "Null listener not supported"); | ||
105 | listeners.remove(listener); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public void entryAdded(IQuerySpecificationRegistryEntry entry) { | ||
110 | if (isEntryRelevant(entry)) { | ||
111 | String fullyQualifiedName = entry.getFullyQualifiedName(); | ||
112 | IMemoryView<IQuerySpecificationRegistryEntry> duplicates = fqnToEntryMap.lookup(fullyQualifiedName); | ||
113 | if(!allowDuplicateFQNs && duplicates != null) { | ||
114 | Set<IQuerySpecificationRegistryEntry> removed = new HashSet<>(duplicates.distinctValues()); | ||
115 | for (IQuerySpecificationRegistryEntry e : removed) { | ||
116 | fqnToEntryMap.removePair(fullyQualifiedName, e); | ||
117 | notifyListeners(e, false); | ||
118 | } | ||
119 | } | ||
120 | fqnToEntryMap.addPair(fullyQualifiedName, entry); | ||
121 | notifyListeners(entry, true); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public void entryRemoved(IQuerySpecificationRegistryEntry entry) { | ||
127 | if (isEntryRelevant(entry)) { | ||
128 | String fullyQualifiedName = entry.getFullyQualifiedName(); | ||
129 | fqnToEntryMap.removePair(fullyQualifiedName, entry); | ||
130 | notifyListeners(entry, false); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | private void notifyListeners(IQuerySpecificationRegistryEntry entry, boolean addition) { | ||
135 | for (IQuerySpecificationRegistryChangeListener listener : listeners) { | ||
136 | try { | ||
137 | if(addition){ | ||
138 | listener.entryAdded(entry); | ||
139 | } else { | ||
140 | listener.entryRemoved(entry); | ||
141 | } | ||
142 | } catch (Exception ex) { | ||
143 | Logger logger = ViatraQueryLoggingUtil.getLogger(AbstractRegistryView.class); | ||
144 | String formatString = addition ? LISTENER_EXCEPTION_ADD : LISTENER_EXCEPTION_REMOVE; | ||
145 | logger.error(String.format(formatString, listener), ex); | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/EcoreIndexHost.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/EcoreIndexHost.java new file mode 100644 index 00000000..e87a726a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/EcoreIndexHost.java | |||
@@ -0,0 +1,166 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.tabular; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import org.eclipse.emf.ecore.EClass; | ||
18 | import org.eclipse.emf.ecore.EClassifier; | ||
19 | import org.eclipse.emf.ecore.EDataType; | ||
20 | import org.eclipse.emf.ecore.EPackage; | ||
21 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
22 | import org.eclipse.emf.ecore.EcorePackage; | ||
23 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
24 | import tools.refinery.viatra.runtime.emf.EMFQueryMetaContext; | ||
25 | import tools.refinery.viatra.runtime.emf.EMFScope; | ||
26 | import tools.refinery.viatra.runtime.emf.types.EClassExactInstancesKey; | ||
27 | import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey; | ||
28 | import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey; | ||
29 | import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey; | ||
30 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
31 | import tools.refinery.viatra.runtime.matchers.scopes.IStorageBackend; | ||
32 | import tools.refinery.viatra.runtime.matchers.scopes.SimpleRuntimeContext; | ||
33 | import tools.refinery.viatra.runtime.matchers.scopes.tables.DisjointUnionTable; | ||
34 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary; | ||
35 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterUnary; | ||
36 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
37 | |||
38 | /** | ||
39 | * Simple EMF-specific demo tabular index host. | ||
40 | * | ||
41 | * <p> Usage: <ul> | ||
42 | * <li> First, instantiate index host with given Ecore metamodel packages | ||
43 | * <li> To emulate an EMF instance model, write arbitrary content into the tables using {@link #getTableDirectInstances(EClassifier)} and {@link #getTableFeatureSlots(EStructuralFeature)}. | ||
44 | * <li> Initialize and evaluate regular EMF-based Viatra queries on the scope provided by {@link #getScope()}, as you would on an {@link EMFScope}. | ||
45 | * <ul> | ||
46 | * | ||
47 | * <p> | ||
48 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
49 | * part of a work in progress. There is no guarantee that this API will | ||
50 | * work or that it will remain the same. | ||
51 | * | ||
52 | * @author Gabor Bergmann | ||
53 | * @since 2.1 | ||
54 | */ | ||
55 | public class EcoreIndexHost extends TabularIndexHost { | ||
56 | |||
57 | public EcoreIndexHost(IStorageBackend storage, EPackage... packages) { | ||
58 | super(storage, new SimpleRuntimeContext(EMFQueryMetaContext.DEFAULT_SURROGATE)); | ||
59 | |||
60 | initTables(packages); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | protected boolean isQueryScopeEmulated(Class<? extends QueryScope> queryScopeClass) { | ||
65 | return EMFScope.class.equals(queryScopeClass); | ||
66 | } | ||
67 | |||
68 | |||
69 | private Map<EClassifier, ITableWriterUnary.Table<Object>> tableDirectInstances = CollectionsFactory.createMap(); | ||
70 | private Map<EClass, DisjointUnionTable> tableTransitiveInstances = CollectionsFactory.createMap(); | ||
71 | private Map<EStructuralFeature, ITableWriterBinary.Table<Object, Object>> tableFeatures = CollectionsFactory.createMap(); | ||
72 | |||
73 | private void initTables(EPackage[] packages) { | ||
74 | |||
75 | // create instance tables first | ||
76 | for (EPackage ePackage : packages) { | ||
77 | for (EClassifier eClassifier : ePackage.getEClassifiers()) { | ||
78 | boolean unique; | ||
79 | IInputKey classifierKey; | ||
80 | if (eClassifier instanceof EClass) { | ||
81 | EClass eClass = (EClass) eClassifier; | ||
82 | |||
83 | // create transitive instances table | ||
84 | IInputKey transitiveKey = new EClassTransitiveInstancesKey(eClass); | ||
85 | DisjointUnionTable transitiveTable = registerNewTable(new DisjointUnionTable(transitiveKey, runtimeContext)); | ||
86 | tableTransitiveInstances.put(eClass, transitiveTable); | ||
87 | |||
88 | // process feature tables | ||
89 | for (EStructuralFeature feature : eClass.getEStructuralFeatures()) { | ||
90 | IInputKey featureKey = new EStructuralFeatureInstancesKey(feature); | ||
91 | ITableWriterBinary.Table<Object, Object> featureTable = newBinaryInputTable(featureKey, feature.isUnique()); | ||
92 | tableFeatures.put(feature, featureTable); | ||
93 | } | ||
94 | |||
95 | // direct instance table | ||
96 | unique = true; | ||
97 | classifierKey = new EClassExactInstancesKey(eClass); | ||
98 | } else { // datatype | ||
99 | unique = false; | ||
100 | classifierKey = new EDataTypeInSlotsKey((EDataType) eClassifier); | ||
101 | } | ||
102 | ITableWriterUnary.Table<Object> directTable = newUnaryInputTable(classifierKey, unique); | ||
103 | tableDirectInstances.put(eClassifier, directTable); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | // global implicit supertype EObject is always available as a transitive table | ||
108 | EClass eObjectClass = EcorePackage.eINSTANCE.getEObject(); | ||
109 | DisjointUnionTable eObjectAllInstancesTable = tableTransitiveInstances.get(eObjectClass); | ||
110 | if (eObjectAllInstancesTable == null) { // is it already added? | ||
111 | IInputKey transitiveKey = new EClassTransitiveInstancesKey(eObjectClass); | ||
112 | eObjectAllInstancesTable = registerNewTable(new DisjointUnionTable(transitiveKey, runtimeContext)); | ||
113 | tableTransitiveInstances.put(eObjectClass, eObjectAllInstancesTable); | ||
114 | |||
115 | boolean unique = true; | ||
116 | IInputKey classifierKey = new EClassExactInstancesKey(eObjectClass); | ||
117 | ITableWriterUnary.Table<Object> directTable = newUnaryInputTable(classifierKey, unique); | ||
118 | tableDirectInstances.put(eObjectClass, directTable); | ||
119 | } | ||
120 | |||
121 | // set up disjoint unoin tables | ||
122 | for (Entry<EClass, DisjointUnionTable> entry : tableTransitiveInstances.entrySet()) { | ||
123 | EClass eClass = entry.getKey(); | ||
124 | ITableWriterUnary.Table<Object> directTable = tableDirectInstances.get(eClass); | ||
125 | |||
126 | // the direct type itself is a child | ||
127 | entry.getValue().addChildTable(directTable); | ||
128 | |||
129 | // connect supertypes | ||
130 | for (EClass superClass : eClass.getEAllSuperTypes()) { | ||
131 | DisjointUnionTable transitiveTable = tableTransitiveInstances.get(superClass); | ||
132 | if (transitiveTable == null) { | ||
133 | throw new IllegalStateException( | ||
134 | String.format("No index table found for EClass %s, supertype of %s", | ||
135 | superClass.getName(), eClass.getName()) | ||
136 | ); | ||
137 | } | ||
138 | transitiveTable.addChildTable(directTable); | ||
139 | } | ||
140 | // global implicit supertype | ||
141 | if (!eClass.equals(eObjectClass)) { | ||
142 | eObjectAllInstancesTable.addChildTable(directTable); | ||
143 | } | ||
144 | |||
145 | } | ||
146 | |||
147 | } | ||
148 | |||
149 | public ITableWriterUnary.Table<Object> getTableDirectInstances(EClassifier classifier) { | ||
150 | return tableDirectInstances.get(classifier); | ||
151 | } | ||
152 | public ITableWriterBinary.Table<Object, Object> getTableFeatureSlots(EStructuralFeature feature) { | ||
153 | return tableFeatures.get(feature); | ||
154 | } | ||
155 | |||
156 | |||
157 | |||
158 | public Set<Entry<EClassifier, ITableWriterUnary.Table<Object>>> getAllCurrentTablesDirectInstances() { | ||
159 | return Collections.unmodifiableSet(tableDirectInstances.entrySet()); | ||
160 | } | ||
161 | public Set<Entry<EStructuralFeature, ITableWriterBinary.Table<Object, Object>>> getAllCurrentTablesFeatures() { | ||
162 | return Collections.unmodifiableSet(tableFeatures.entrySet()); | ||
163 | } | ||
164 | |||
165 | |||
166 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularEngineContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularEngineContext.java new file mode 100644 index 00000000..ee33d3bc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularEngineContext.java | |||
@@ -0,0 +1,107 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
3 | * This program and the accompanying materials are made available under the | ||
4 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
5 | * http://www.eclipse.org/legal/epl-v20.html. | ||
6 | * | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | |||
10 | package tools.refinery.viatra.runtime.tabular; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
14 | import tools.refinery.viatra.runtime.api.scope.*; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
17 | |||
18 | import java.lang.reflect.InvocationTargetException; | ||
19 | import java.util.List; | ||
20 | import java.util.concurrent.Callable; | ||
21 | |||
22 | /** | ||
23 | * @author Gabor Bergmann | ||
24 | * | ||
25 | * @since 2.1 | ||
26 | */ | ||
27 | class TabularEngineContext implements IEngineContext, IBaseIndex { | ||
28 | |||
29 | private TabularIndexHost indexHost; | ||
30 | private ViatraQueryEngine engine; | ||
31 | private Logger logger; | ||
32 | |||
33 | private List<IIndexingErrorListener> errorListeners = CollectionsFactory.createObserverList(); | ||
34 | |||
35 | public TabularEngineContext(TabularIndexHost server, ViatraQueryEngine engine, | ||
36 | IIndexingErrorListener errorListener, Logger logger) { | ||
37 | this.indexHost = server; | ||
38 | this.engine = engine; | ||
39 | this.logger = logger; | ||
40 | |||
41 | this.addIndexingErrorListener(errorListener); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public IBaseIndex getBaseIndex() { | ||
46 | return this; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void dispose() { | ||
51 | // NOP, server lifecycle not controlled by engine | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public IQueryRuntimeContext getQueryRuntimeContext() { | ||
56 | return indexHost.getRuntimeContext(); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException { | ||
61 | try { | ||
62 | return callable.call(); | ||
63 | } catch (Exception e) { | ||
64 | throw new InvocationTargetException(e); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { | ||
70 | // TODO no notifications yet | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) { | ||
75 | // TODO no notifications yet | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) { | ||
80 | // TODO no notifications yet | ||
81 | return true; | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) { | ||
86 | // TODO no notifications yet | ||
87 | return true; | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public void resampleDerivedFeatures() { | ||
92 | throw new UnsupportedOperationException(); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public boolean addIndexingErrorListener(IIndexingErrorListener listener) { | ||
97 | return errorListeners.add(listener); | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public boolean removeIndexingErrorListener(IIndexingErrorListener listener) { | ||
102 | return errorListeners.remove(listener); | ||
103 | } | ||
104 | |||
105 | |||
106 | |||
107 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularIndexHost.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularIndexHost.java new file mode 100644 index 00000000..6f2a1f5f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularIndexHost.java | |||
@@ -0,0 +1,145 @@ | |||
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.tabular; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.api.ViatraQueryEngine; | ||
12 | import tools.refinery.viatra.runtime.api.scope.IEngineContext; | ||
13 | import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener; | ||
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.scopes.IStorageBackend; | ||
17 | import tools.refinery.viatra.runtime.matchers.scopes.TabularRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable; | ||
19 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary; | ||
20 | import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterUnary; | ||
21 | |||
22 | /** | ||
23 | * Simple tabular index host. | ||
24 | * | ||
25 | * Unlike traditional Viatra instances initialized on a model, | ||
26 | * this index host can be initialized and then queried, | ||
27 | * while its queriable "contents" (base relations) can be separately written using table writer API. | ||
28 | * | ||
29 | * <p> Deriving classes are responsible for setting up the tables of this index and providing the writer API to clients. | ||
30 | * For the former, use {@link #newUnaryInputTable(IInputKey, boolean)}, | ||
31 | * {@link #newBinaryInputTable(IInputKey, boolean)} to create input tables, | ||
32 | * or {@link #registerNewTable(IIndexTable)} to register manually created derived tables. | ||
33 | * Instantiate such tables with {@link #runtimeContext} as their table context. | ||
34 | * | ||
35 | * | ||
36 | * <p> | ||
37 | * <strong>EXPERIMENTAL</strong>. This class or interface has been added as | ||
38 | * part of a work in progress. There is no guarantee that this API will | ||
39 | * work or that it will remain the same. | ||
40 | * | ||
41 | * @see EcoreIndexHost EcoreIndexHost for EMF-specific example usage. | ||
42 | * | ||
43 | * @author Gabor Bergmann | ||
44 | * @since 2.1 | ||
45 | */ | ||
46 | public abstract class TabularIndexHost { | ||
47 | |||
48 | private final IStorageBackend storage; | ||
49 | protected final TabularRuntimeContext runtimeContext; | ||
50 | protected final TabularIndexScope scope = new TabularIndexScope(); | ||
51 | |||
52 | public TabularIndexHost(IStorageBackend storage, TabularRuntimeContext runtimeContext) { | ||
53 | this.storage = storage; | ||
54 | this.runtimeContext = runtimeContext; | ||
55 | } | ||
56 | |||
57 | |||
58 | public TabularRuntimeContext getRuntimeContext() { | ||
59 | return runtimeContext; | ||
60 | } | ||
61 | |||
62 | public TabularIndexScope getScope() { | ||
63 | return scope; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @return true if this index host aims to serve queries that have a scope of the given type | ||
68 | */ | ||
69 | protected abstract boolean isQueryScopeEmulated(Class<? extends QueryScope> queryScopeClass); | ||
70 | |||
71 | |||
72 | |||
73 | /** | ||
74 | * Marks the beginning of an update transaction. | ||
75 | * In transaction mode, index table updates may be temporarily delayed for better performance. | ||
76 | * Stateful query backends will not update their results during the index update transaction. (TODO actually achieve this) | ||
77 | */ | ||
78 | public void startUpdateTransaction() { | ||
79 | storage.startTransaction(); | ||
80 | } | ||
81 | /** | ||
82 | * Marks the end of an update transaction. | ||
83 | * Any updates to index tables that were delayed during the transaction must now be flushed. | ||
84 | * Afterwards, stateful query backends will update their results. (TODO actually achieve this) | ||
85 | */ | ||
86 | public void finishUpdateTransaction() { | ||
87 | storage.finishTransaction(); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * To be called by deriving class. Creates and registers a new unary input table. | ||
92 | */ | ||
93 | protected ITableWriterUnary.Table<Object> newUnaryInputTable(IInputKey key, boolean unique) { | ||
94 | return registerNewTable(storage.createUnaryTable(key, runtimeContext, unique)); | ||
95 | } | ||
96 | /** | ||
97 | * To be called by deriving class. Creates and registers a new binary input table. | ||
98 | */ | ||
99 | protected ITableWriterBinary.Table<Object,Object> newBinaryInputTable(IInputKey key, boolean unique) { | ||
100 | return registerNewTable(storage.createBinaryTable(key, runtimeContext, unique)); | ||
101 | } | ||
102 | /** | ||
103 | * To be called by deriving class. Registers the given freshly created table and also returns it for convenience. | ||
104 | */ | ||
105 | protected <Table extends IIndexTable> Table registerNewTable(Table newTable) { | ||
106 | runtimeContext.registerIndexTable(newTable); | ||
107 | return newTable; | ||
108 | } | ||
109 | |||
110 | |||
111 | /** | ||
112 | * A scope describing queries evaluated against tzhis index host. | ||
113 | * @author Gabor Bergmann | ||
114 | * | ||
115 | */ | ||
116 | public class TabularIndexScope extends QueryScope { | ||
117 | |||
118 | public TabularIndexHost getIndexHost() { | ||
119 | return TabularIndexHost.this; | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public int hashCode() { | ||
124 | return getIndexHost().hashCode(); | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public boolean equals(Object obj) { | ||
129 | if (obj instanceof TabularIndexScope) | ||
130 | return getIndexHost().equals(((TabularIndexScope) obj).getIndexHost()); | ||
131 | return false; | ||
132 | } | ||
133 | |||
134 | @Override | ||
135 | public boolean isCompatibleWithQueryScope(Class<? extends QueryScope> queryScopeClass) { | ||
136 | return isQueryScopeEmulated(queryScopeClass) || super.isCompatibleWithQueryScope(queryScopeClass); | ||
137 | } | ||
138 | |||
139 | @Override | ||
140 | protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, org.apache.log4j.Logger logger) { | ||
141 | return new TabularEngineContext(getIndexHost(), engine, errorListener, logger); | ||
142 | } | ||
143 | |||
144 | } | ||
145 | } | ||
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 | } | ||