diff options
author | 2023-08-19 02:31:57 +0200 | |
---|---|---|
committer | 2023-08-19 02:31:57 +0200 | |
commit | 9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b (patch) | |
tree | fad77963c1dc9028b0a767ac3ad69a174c8eb8c6 /subprojects/viatra-runtime | |
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')
95 files changed, 10085 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/about.html b/subprojects/viatra-runtime/about.html new file mode 100644 index 00000000..d1d5593a --- /dev/null +++ b/subprojects/viatra-runtime/about.html | |||
@@ -0,0 +1,26 @@ | |||
1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> | ||
2 | <html> | ||
3 | <!-- | ||
4 | Copyright (c) 2017, Eclipse.org Foundation, Inc. | ||
5 | |||
6 | SPDX-License-Identifier: LicenseRef-EPL-Steward | ||
7 | --> | ||
8 | <head> | ||
9 | <title>About</title> | ||
10 | <meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1"> | ||
11 | </head> | ||
12 | <body lang="EN-US"> | ||
13 | <h2>About This Content</h2> | ||
14 | |||
15 | <p>March 18, 2019</p> | ||
16 | <h3>License</h3> | ||
17 | |||
18 | <p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the | ||
19 | Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>. | ||
20 | For purposes of the EPL, "Program" will mean the Content.</p> | ||
21 | |||
22 | <p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may | ||
23 | apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise | ||
24 | indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> | ||
25 | </body> | ||
26 | </html> | ||
diff --git a/subprojects/viatra-runtime/build.gradle.kts b/subprojects/viatra-runtime/build.gradle.kts new file mode 100644 index 00000000..5d6e3de6 --- /dev/null +++ b/subprojects/viatra-runtime/build.gradle.kts | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | plugins { | ||
8 | id("tools.refinery.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | dependencies { | ||
12 | api(project(":refinery-viatra-runtime-base")) | ||
13 | api(libs.ecore) | ||
14 | implementation(libs.eclipse) | ||
15 | implementation(libs.emf) | ||
16 | implementation(libs.slf4j.log4j) | ||
17 | } | ||
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 | } | ||