aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime/src/main')
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java24
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/ViatraQueryRuntimePlugin.java32
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/AdvancedViatraQueryEngine.java368
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatch.java166
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericPatternMatcher.java83
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQueryGroup.java82
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/GenericQuerySpecification.java76
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IMatchUpdateListener.java37
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IModelConnectorTypeEnum.java18
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IPatternMatch.java107
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQueryGroup.java46
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IQuerySpecification.java87
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/IRunOnceQueryEngine.java68
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/LazyLoadingQueryGroup.java64
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/MatchUpdateAdapter.java105
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/PackageBasedQueryGroup.java133
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java152
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineInitializationListener.java26
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineLifecycleListener.java52
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineManager.java196
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngineOptions.java293
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryMatcher.java258
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryModelUpdateListener.java55
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFPQuery.java110
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecification.java40
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedEMFQuerySpecificationWithGenericMatcher.java58
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseGeneratedPatternGroup.java31
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseMatcher.java350
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BasePatternMatch.java115
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQueryGroup.java33
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/BaseQuerySpecification.java147
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/impl/RunOnceQueryEngine.java140
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IBaseIndex.java91
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IEngineContext.java49
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IIndexingErrorListener.java25
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/IInstanceObserver.java21
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/QueryScope.java33
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/scope/ViatraBaseIndexChangeListener.java34
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/DynamicEMFQueryRuntimeContext.java47
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFBaseIndexWrapper.java160
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFEngineContext.java110
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryMetaContext.java405
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFQueryRuntimeContext.java839
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/EMFScope.java199
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/helper/ViatraQueryRuntimeHelper.java161
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/BaseEMFTypeKey.java34
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassExactInstancesKey.java51
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassTransitiveInstancesKey.java47
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EClassUnscopedTransitiveInstancesKey.java46
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EDataTypeInSlotsKey.java48
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/emf/types/EStructuralFeatureInstancesKey.java48
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/exception/ViatraQueryException.java71
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQueryGroupProvider.java40
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/IQuerySpecificationProvider.java36
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/PQueryExtensionFactory.java33
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonExtensionFactory.java62
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQueryGroupProvider.java46
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/SingletonQuerySpecificationProvider.java42
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/extensibility/ViatraQueryRuntimeConstants.java27
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSurrogateQueryLoader.java148
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/ExtensionBasedSystemDefaultBackendLoader.java60
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/EngineContextFactory.java24
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/QueryResultWrapper.java32
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java705
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/LifecycleProvider.java138
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ListenerContainer.java43
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/engine/ModelUpdateProvider.java214
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/ExtensionBasedQuerySpecificationLoader.java303
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IConnectorListener.java40
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IDefaultRegistryView.java37
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistry.java74
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryChangeListener.java35
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IQuerySpecificationRegistryEntry.java48
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistrySourceConnector.java50
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryView.java72
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFactory.java34
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/IRegistryViewFilter.java33
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/QuerySpecificationRegistry.java112
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/AbstractRegistrySourceConnector.java81
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/QueryGroupProviderSourceConnector.java80
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/connector/SpecificationMapSourceConnector.java147
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/QuerySpecificationStore.java38
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistryEntryImpl.java81
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/data/RegistrySourceImpl.java73
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/FilteringRegistryView.java43
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/GlobalRegistryView.java65
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/QuerySpecificationRegistryImpl.java177
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/impl/RegistryChangeMultiplexer.java58
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/registry/view/AbstractRegistryView.java150
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/EcoreIndexHost.java166
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularEngineContext.java107
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/tabular/TabularIndexHost.java145
-rw-r--r--subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/util/ViatraQueryLoggingUtil.java72
93 files changed, 10042 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java
new file mode 100644
index 00000000..d5e0d51f
--- /dev/null
+++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java
@@ -0,0 +1,24 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package 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 */
18public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime;
10
11import org.eclipse.core.runtime.Plugin;
12import tools.refinery.viatra.runtime.internal.ExtensionBasedSurrogateQueryLoader;
13import tools.refinery.viatra.runtime.internal.ExtensionBasedSystemDefaultBackendLoader;
14import tools.refinery.viatra.runtime.registry.ExtensionBasedQuerySpecificationLoader;
15import org.osgi.framework.BundleContext;
16
17/**
18 * The activator class controls the plug-in life cycle
19 */
20public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.concurrent.Callable;
13
14import tools.refinery.viatra.runtime.api.scope.QueryScope;
15import tools.refinery.viatra.runtime.emf.EMFScope;
16import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl;
17import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
18import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
19import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
22import 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 */
52public 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
10package tools.refinery.viatra.runtime.api;
11
12import java.util.Arrays;
13
14import 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 */
25public 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
10package tools.refinery.viatra.runtime.api;
11
12import tools.refinery.viatra.runtime.api.impl.BaseMatcher;
13import 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 */
32public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import java.util.Arrays;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.stream.Collectors;
15import java.util.stream.Stream;
16
17import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import tools.refinery.viatra.runtime.api.impl.BaseQuerySpecification;
12import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
13import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
14import 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 */
39public 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 *******************************************************************************/
9package 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 */
21public 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 *******************************************************************************/
9package 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 */
14public 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
10package tools.refinery.viatra.runtime.api;
11
12import 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 */
23public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import 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 */
22public 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
10package tools.refinery.viatra.runtime.api;
11
12import tools.refinery.viatra.runtime.api.scope.QueryScope;
13import tools.refinery.viatra.runtime.emf.EMFScope;
14import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
15import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
16import 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 */
27public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import java.util.Collection;
12
13import org.eclipse.emf.common.notify.Notifier;
14import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
15import 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 */
25public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import java.util.Collections;
12import java.util.Objects;
13import java.util.Set;
14import java.util.function.Supplier;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.api.impl.BaseQueryGroup;
18import tools.refinery.viatra.runtime.matchers.util.Preconditions;
19import 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 */
29public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import 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 */
23public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.api.impl.BaseQueryGroup;
16import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
17import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry;
18import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener;
19import tools.refinery.viatra.runtime.registry.IRegistryView;
20import tools.refinery.viatra.runtime.registry.IRegistryViewFilter;
21import 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 */
29public 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
10package tools.refinery.viatra.runtime.api;
11
12import org.eclipse.emf.common.notify.Notifier;
13import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
14import tools.refinery.viatra.runtime.api.scope.QueryScope;
15import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
16import tools.refinery.viatra.runtime.emf.EMFScope;
17import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
18
19import java.util.Set;
20import 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 */
47public 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 *******************************************************************************/
9package 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 */
17public 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 *******************************************************************************/
9package 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 */
21public 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
10package tools.refinery.viatra.runtime.api;
11
12import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument;
13
14import java.lang.ref.WeakReference;
15import java.util.Collections;
16import java.util.HashSet;
17import java.util.Map;
18import java.util.Set;
19import java.util.WeakHashMap;
20
21import tools.refinery.viatra.runtime.api.scope.QueryScope;
22import tools.refinery.viatra.runtime.emf.EMFScope;
23import tools.refinery.viatra.runtime.internal.apiimpl.ViatraQueryEngineImpl;
24import 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 */
44public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api;
10
11
12import java.util.Objects;
13import java.util.ServiceLoader;
14
15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
16import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
17import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
18import 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 */
30public 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
10package tools.refinery.viatra.runtime.api;
11
12import java.util.Collection;
13import java.util.List;
14import java.util.Optional;
15import java.util.Set;
16import java.util.function.Consumer;
17import 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 */
27public 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 *******************************************************************************/
9package 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 */
19public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.impl;
10
11import org.eclipse.emf.ecore.EClass;
12import org.eclipse.emf.ecore.EClassifier;
13import org.eclipse.emf.ecore.EEnum;
14import org.eclipse.emf.ecore.EEnumLiteral;
15import org.eclipse.emf.ecore.EPackage;
16import org.eclipse.emf.ecore.EStructuralFeature;
17import tools.refinery.viatra.runtime.exception.ViatraQueryException;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.BasePQuery;
19import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException;
21
22/**
23 * Common superclass for EMF-based generated PQueries.
24 * @author Bergmann Gabor
25 *
26 */
27public 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
10package tools.refinery.viatra.runtime.api.impl;
11
12import tools.refinery.viatra.runtime.api.IPatternMatch;
13import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
14import tools.refinery.viatra.runtime.api.scope.QueryScope;
15import tools.refinery.viatra.runtime.emf.EMFScope;
16import 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 */
24public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.impl;
10
11import tools.refinery.viatra.runtime.api.GenericPatternMatch;
12import tools.refinery.viatra.runtime.api.GenericPatternMatcher;
13import tools.refinery.viatra.runtime.api.GenericQuerySpecification;
14import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
15import tools.refinery.viatra.runtime.api.scope.QueryScope;
16import tools.refinery.viatra.runtime.emf.EMFScope;
17import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.impl;
10
11import java.util.HashSet;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.api.IQuerySpecification;
15
16/**
17 * @author Mark Czotter
18 *
19 */
20public 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
10package tools.refinery.viatra.runtime.api.impl;
11
12import java.util.Collection;
13import java.util.List;
14import java.util.Optional;
15import java.util.Set;
16import java.util.function.Consumer;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20import tools.refinery.viatra.runtime.api.IPatternMatch;
21import tools.refinery.viatra.runtime.api.IQuerySpecification;
22import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
23import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
24import tools.refinery.viatra.runtime.internal.apiimpl.QueryResultWrapper;
25import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
26import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
27import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
28import 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 */
37public 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
10package tools.refinery.viatra.runtime.api.impl;
11
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.List;
15
16import org.eclipse.emf.ecore.EObject;
17import org.eclipse.emf.ecore.EStructuralFeature;
18import tools.refinery.viatra.runtime.api.IPatternMatch;
19
20/**
21 * Base implementation of IPatternMatch.
22 *
23 * @author Bergmann Gábor
24 *
25 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.impl;
10
11import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
12import tools.refinery.viatra.runtime.api.IQueryGroup;
13import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
14
15/**
16 * Base implementation of {@link IQueryGroup}.
17 *
18 * @author Mark Czotter
19 *
20 */
21public 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
10package tools.refinery.viatra.runtime.api.impl;
11
12import java.util.List;
13import java.util.Optional;
14import java.util.stream.Collectors;
15
16import tools.refinery.viatra.runtime.api.IPatternMatch;
17import tools.refinery.viatra.runtime.api.IQuerySpecification;
18import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
19import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
20import tools.refinery.viatra.runtime.exception.ViatraQueryException;
21import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation;
22import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
23import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
24import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException;
25import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
26import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
27
28/**
29 * Base implementation of IQuerySpecification.
30 *
31 * @author Gabor Bergmann
32 *
33 */
34public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.impl;
10
11import java.util.Collection;
12
13import org.eclipse.emf.common.notify.Notifier;
14import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
15import tools.refinery.viatra.runtime.api.IPatternMatch;
16import tools.refinery.viatra.runtime.api.IQuerySpecification;
17import tools.refinery.viatra.runtime.api.IRunOnceQueryEngine;
18import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
19import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
20import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener;
21import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
22import 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 */
32public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.scope;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.concurrent.Callable;
13
14/**
15 * Represents the index maintained on the model.
16 * @author Bergmann Gabor
17 * @since 0.9
18 *
19 */
20public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.scope;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12import 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 */
21public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.scope;
10
11import 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 */
21public 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 *******************************************************************************/
9package 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 */
18public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.api.scope;
10
11import tools.refinery.viatra.runtime.api.IQuerySpecification;
12import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
13import 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 */
21public 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 *******************************************************************************/
9package 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 */
18public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.base.api.NavigationHelper;
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.HashMap;
13import java.util.Map;
14import java.util.concurrent.Callable;
15
16import org.eclipse.emf.common.notify.Notification;
17import org.eclipse.emf.ecore.EObject;
18import org.eclipse.emf.ecore.EStructuralFeature;
19import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
20import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
21import tools.refinery.viatra.runtime.api.scope.IInstanceObserver;
22import tools.refinery.viatra.runtime.api.scope.ViatraBaseIndexChangeListener;
23import tools.refinery.viatra.runtime.base.api.EMFBaseIndexChangeListener;
24import tools.refinery.viatra.runtime.base.api.IEMFIndexingErrorListener;
25import tools.refinery.viatra.runtime.base.api.LightweightEObjectObserver;
26import 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 */
33public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import org.apache.log4j.Logger;
12import org.eclipse.emf.common.notify.Notifier;
13import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
14import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
15import tools.refinery.viatra.runtime.api.scope.IEngineContext;
16import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
17import tools.refinery.viatra.runtime.base.api.ViatraBaseFactory;
18import tools.refinery.viatra.runtime.base.api.NavigationHelper;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
21
22/**
23 * Implements an engine context on EMF models.
24 * @author Bergmann Gabor
25 *
26 */
27class 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.Map;
18import java.util.Set;
19
20import org.eclipse.emf.common.util.EList;
21import org.eclipse.emf.ecore.EAttribute;
22import org.eclipse.emf.ecore.EClass;
23import org.eclipse.emf.ecore.EClassifier;
24import org.eclipse.emf.ecore.EDataType;
25import org.eclipse.emf.ecore.EObject;
26import org.eclipse.emf.ecore.EReference;
27import org.eclipse.emf.ecore.EStructuralFeature;
28import org.eclipse.emf.ecore.EcorePackage;
29import tools.refinery.viatra.runtime.emf.types.BaseEMFTypeKey;
30import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
31import tools.refinery.viatra.runtime.emf.types.EClassUnscopedTransitiveInstancesKey;
32import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
33import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
34import tools.refinery.viatra.runtime.matchers.context.AbstractQueryMetaContext;
35import tools.refinery.viatra.runtime.matchers.context.IInputKey;
36import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication;
37import 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 */
49public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import java.lang.reflect.InvocationTargetException;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.EnumSet;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.Map;
18import java.util.Optional;
19import java.util.Set;
20import java.util.concurrent.Callable;
21import java.util.function.Function;
22import java.util.stream.Collectors;
23
24import org.apache.log4j.Logger;
25import org.eclipse.emf.ecore.EClass;
26import org.eclipse.emf.ecore.EDataType;
27import org.eclipse.emf.ecore.EObject;
28import org.eclipse.emf.ecore.EStructuralFeature;
29import tools.refinery.viatra.runtime.base.api.DataTypeListener;
30import tools.refinery.viatra.runtime.base.api.FeatureListener;
31import tools.refinery.viatra.runtime.base.api.IndexingLevel;
32import tools.refinery.viatra.runtime.base.api.InstanceListener;
33import tools.refinery.viatra.runtime.base.api.NavigationHelper;
34import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
35import tools.refinery.viatra.runtime.emf.types.EClassUnscopedTransitiveInstancesKey;
36import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
37import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
38import tools.refinery.viatra.runtime.matchers.context.AbstractQueryRuntimeContext;
39import tools.refinery.viatra.runtime.matchers.context.IInputKey;
40import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
41import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
42import tools.refinery.viatra.runtime.matchers.context.IndexingService;
43import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey;
44import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
45import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
46import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
47import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
48import 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 */
58public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf;
10
11import java.util.Arrays;
12import java.util.Collections;
13import java.util.HashSet;
14import java.util.Set;
15import java.util.function.Predicate;
16import java.util.stream.Collectors;
17
18import org.apache.log4j.Logger;
19import org.eclipse.emf.common.notify.Notifier;
20import org.eclipse.emf.common.util.URI;
21import org.eclipse.emf.ecore.EObject;
22import org.eclipse.emf.ecore.resource.Resource;
23import org.eclipse.emf.ecore.resource.ResourceSet;
24import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
25import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
26import tools.refinery.viatra.runtime.api.scope.IEngineContext;
27import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
28import tools.refinery.viatra.runtime.api.scope.QueryScope;
29import tools.refinery.viatra.runtime.base.api.BaseIndexOptions;
30import tools.refinery.viatra.runtime.base.api.NavigationHelper;
31import 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 */
48public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.helper;
10
11import java.util.function.Function;
12
13import org.eclipse.emf.ecore.EClassifier;
14import org.eclipse.emf.ecore.EObject;
15import org.eclipse.emf.ecore.EPackage;
16import org.eclipse.emf.ecore.EStructuralFeature;
17import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.types;
10
11import tools.refinery.viatra.runtime.matchers.context.common.BaseInputKeyWrapper;
12
13/**
14 * Base class for EMF Type keys.
15 * @author Bergmann Gabor
16 *
17 */
18public 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
10package tools.refinery.viatra.runtime.emf.types;
11
12import org.eclipse.emf.ecore.EClass;
13import tools.refinery.viatra.runtime.emf.EMFScope;
14import 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 */
25public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.types;
10
11import org.eclipse.emf.ecore.EClass;
12import tools.refinery.viatra.runtime.emf.EMFScope;
13import 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 */
21public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.types;
10
11import org.eclipse.emf.ecore.EClass;
12import 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 */
20public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.types;
10
11import org.eclipse.emf.ecore.EDataType;
12import 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 */
19public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.emf.types;
10
11import org.eclipse.emf.ecore.EStructuralFeature;
12import tools.refinery.viatra.runtime.emf.EMFScope;
13import 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 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.exception;
10
11import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
12import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
13import 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 */
23public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import java.util.Set;
12
13import tools.refinery.viatra.runtime.api.IQueryGroup;
14import 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 */
24public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import tools.refinery.viatra.runtime.api.IQuerySpecification;
12import 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 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import org.eclipse.core.runtime.CoreException;
12import org.eclipse.core.runtime.IStatus;
13import org.eclipse.core.runtime.Status;
14import 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 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import java.lang.reflect.Method;
12
13import org.eclipse.core.runtime.CoreException;
14import org.eclipse.core.runtime.IConfigurationElement;
15import org.eclipse.core.runtime.IExecutableExtension;
16import org.eclipse.core.runtime.IExecutableExtensionFactory;
17import org.eclipse.core.runtime.IStatus;
18import org.eclipse.core.runtime.Platform;
19import org.eclipse.core.runtime.Status;
20import 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 */
28public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import java.util.Set;
12import java.util.stream.Collectors;
13
14import tools.refinery.viatra.runtime.api.IQueryGroup;
15import tools.refinery.viatra.runtime.api.IQuerySpecification;
16import 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 */
25public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.extensibility;
10
11import tools.refinery.viatra.runtime.api.IQuerySpecification;
12import 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 */
21public 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 *******************************************************************************/
9package 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 */
17public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal;
10
11import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkState;
12
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.HashMap;
16import java.util.Map;
17import java.util.Map.Entry;
18
19import org.apache.log4j.Logger;
20import org.eclipse.core.runtime.CoreException;
21import org.eclipse.core.runtime.IConfigurationElement;
22import org.eclipse.core.runtime.Platform;
23import org.eclipse.emf.ecore.EClass;
24import org.eclipse.emf.ecore.EClassifier;
25import org.eclipse.emf.ecore.EPackage;
26import org.eclipse.emf.ecore.EStructuralFeature;
27import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
28import tools.refinery.viatra.runtime.extensibility.ViatraQueryRuntimeConstants;
29import tools.refinery.viatra.runtime.matchers.context.IInputKey;
30import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
32import tools.refinery.viatra.runtime.matchers.util.IProvider;
33
34/**
35 * @author Abel Hegedus
36 *
37 */
38public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal;
10
11import org.eclipse.core.runtime.CoreException;
12import org.eclipse.core.runtime.IConfigurationElement;
13import org.eclipse.core.runtime.Platform;
14import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions;
15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
16import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
17import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil;
18
19/**
20 * @since 2.0
21 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal.apiimpl;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
13import tools.refinery.viatra.runtime.api.scope.IEngineContext;
14import 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 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal.apiimpl;
10
11import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
12import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
13import 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 */
26public 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
10package tools.refinery.viatra.runtime.internal.apiimpl;
11
12import org.apache.log4j.Logger;
13import tools.refinery.viatra.runtime.api.*;
14import tools.refinery.viatra.runtime.api.impl.BaseMatcher;
15import tools.refinery.viatra.runtime.api.scope.IBaseIndex;
16import tools.refinery.viatra.runtime.api.scope.IEngineContext;
17import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
18import tools.refinery.viatra.runtime.api.scope.QueryScope;
19import tools.refinery.viatra.runtime.exception.ViatraQueryException;
20import tools.refinery.viatra.runtime.internal.engine.LifecycleProvider;
21import tools.refinery.viatra.runtime.internal.engine.ModelUpdateProvider;
22import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
23import tools.refinery.viatra.runtime.matchers.backend.*;
24import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
25import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess;
27import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
28import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
29import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PQueries;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
32import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus;
33import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
34import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
35import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
36import tools.refinery.viatra.runtime.matchers.util.Preconditions;
37import tools.refinery.viatra.runtime.registry.IDefaultRegistryView;
38import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
39import tools.refinery.viatra.runtime.registry.QuerySpecificationRegistry;
40import tools.refinery.viatra.runtime.util.ViatraQueryLoggingUtil;
41
42import java.lang.ref.WeakReference;
43import java.lang.reflect.InvocationTargetException;
44import java.util.*;
45import java.util.concurrent.Callable;
46import java.util.stream.Collectors;
47
48import 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 */
55public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal.engine;
10
11import java.util.ArrayList;
12
13import org.apache.log4j.Logger;
14import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
15import tools.refinery.viatra.runtime.api.IPatternMatch;
16import tools.refinery.viatra.runtime.api.ViatraQueryEngineLifecycleListener;
17import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
18
19public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal.engine;
10
11import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument;
12
13import java.util.HashSet;
14import java.util.Set;
15
16public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.internal.engine;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.EnumMap;
15import java.util.HashSet;
16import java.util.Map;
17import java.util.Map.Entry;
18
19import org.apache.log4j.Logger;
20import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine;
21import tools.refinery.viatra.runtime.api.IMatchUpdateListener;
22import tools.refinery.viatra.runtime.api.IPatternMatch;
23import tools.refinery.viatra.runtime.api.ViatraQueryEngineLifecycleListener;
24import tools.refinery.viatra.runtime.api.ViatraQueryMatcher;
25import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener;
26import tools.refinery.viatra.runtime.api.ViatraQueryModelUpdateListener.ChangeLevel;
27import tools.refinery.viatra.runtime.api.scope.ViatraBaseIndexChangeListener;
28import tools.refinery.viatra.runtime.exception.ViatraQueryException;
29import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
30
31public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.Map;
14import java.util.Objects;
15import java.util.Set;
16
17import org.eclipse.core.runtime.CoreException;
18import org.eclipse.core.runtime.IConfigurationElement;
19import org.eclipse.core.runtime.Platform;
20import tools.refinery.viatra.runtime.IExtensions;
21import tools.refinery.viatra.runtime.api.IQueryGroup;
22import tools.refinery.viatra.runtime.api.IQuerySpecification;
23import tools.refinery.viatra.runtime.extensibility.IQueryGroupProvider;
24import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider;
25import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
26import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
27import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
28import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
29import 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 */
44public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import 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 */
21public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import java.util.NoSuchElementException;
12
13import 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 */
23public 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 *******************************************************************************/
9package 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 */
20public 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 *******************************************************************************/
9package 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 */
19public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import 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 */
21public 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 *******************************************************************************/
9package 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 */
20public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import 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 */
28public 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 *******************************************************************************/
9package 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 */
22public 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 *******************************************************************************/
9package 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 */
18public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry;
10
11import java.util.HashSet;
12import java.util.Iterator;
13import java.util.Set;
14
15import 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 */
29public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.connector;
10
11import java.util.HashSet;
12import java.util.Objects;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.registry.IConnectorListener;
16import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.connector;
10
11import java.util.Objects;
12
13import tools.refinery.viatra.runtime.extensibility.IQueryGroupProvider;
14import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider;
15import 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 */
26public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.connector;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.NoSuchElementException;
16import java.util.Objects;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider;
20import tools.refinery.viatra.runtime.extensibility.SingletonQuerySpecificationProvider;
21import 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 */
31public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.data;
10
11import java.util.Map;
12import 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 */
21public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.data;
10
11import tools.refinery.viatra.runtime.api.IQuerySpecification;
12import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider;
13import 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 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.data;
10
11import java.util.Map;
12import 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 */
21public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.impl;
10
11import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
12import tools.refinery.viatra.runtime.registry.IRegistryViewFilter;
13import tools.refinery.viatra.runtime.registry.view.AbstractRegistryView;
14import 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 */
23public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.impl;
10
11import java.util.NoSuchElementException;
12import java.util.Set;
13import java.util.stream.Collectors;
14
15import tools.refinery.viatra.runtime.api.IQueryGroup;
16import tools.refinery.viatra.runtime.api.LazyLoadingQueryGroup;
17import tools.refinery.viatra.runtime.registry.IDefaultRegistryView;
18import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
19import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry;
20import 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 */
28public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.impl;
10
11import static tools.refinery.viatra.runtime.matchers.util.Preconditions.checkArgument;
12
13import java.util.Map;
14
15import org.apache.log4j.Logger;
16import tools.refinery.viatra.runtime.extensibility.IQuerySpecificationProvider;
17import tools.refinery.viatra.runtime.registry.IConnectorListener;
18import tools.refinery.viatra.runtime.registry.IDefaultRegistryView;
19import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
20import tools.refinery.viatra.runtime.registry.IRegistryView;
21import tools.refinery.viatra.runtime.registry.IRegistryViewFactory;
22import tools.refinery.viatra.runtime.registry.IRegistryViewFilter;
23import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener;
24import tools.refinery.viatra.runtime.registry.IRegistrySourceConnector;
25import tools.refinery.viatra.runtime.registry.data.QuerySpecificationStore;
26import tools.refinery.viatra.runtime.registry.data.RegistryEntryImpl;
27import tools.refinery.viatra.runtime.registry.data.RegistrySourceImpl;
28import 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 */
39public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.impl;
10
11import java.util.Collections;
12import java.util.Set;
13import java.util.WeakHashMap;
14
15import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener;
16import 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 */
25public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.registry.view;
10
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.stream.Collectors;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
19import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
20import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
21import tools.refinery.viatra.runtime.matchers.util.Preconditions;
22import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistry;
23import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryChangeListener;
24import tools.refinery.viatra.runtime.registry.IQuerySpecificationRegistryEntry;
25import tools.refinery.viatra.runtime.registry.IRegistryView;
26import 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 */
36public 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
10package tools.refinery.viatra.runtime.tabular;
11
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16
17import org.eclipse.emf.ecore.EClass;
18import org.eclipse.emf.ecore.EClassifier;
19import org.eclipse.emf.ecore.EDataType;
20import org.eclipse.emf.ecore.EPackage;
21import org.eclipse.emf.ecore.EStructuralFeature;
22import org.eclipse.emf.ecore.EcorePackage;
23import tools.refinery.viatra.runtime.api.scope.QueryScope;
24import tools.refinery.viatra.runtime.emf.EMFQueryMetaContext;
25import tools.refinery.viatra.runtime.emf.EMFScope;
26import tools.refinery.viatra.runtime.emf.types.EClassExactInstancesKey;
27import tools.refinery.viatra.runtime.emf.types.EClassTransitiveInstancesKey;
28import tools.refinery.viatra.runtime.emf.types.EDataTypeInSlotsKey;
29import tools.refinery.viatra.runtime.emf.types.EStructuralFeatureInstancesKey;
30import tools.refinery.viatra.runtime.matchers.context.IInputKey;
31import tools.refinery.viatra.runtime.matchers.scopes.IStorageBackend;
32import tools.refinery.viatra.runtime.matchers.scopes.SimpleRuntimeContext;
33import tools.refinery.viatra.runtime.matchers.scopes.tables.DisjointUnionTable;
34import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary;
35import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterUnary;
36import 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 */
55public 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
10package tools.refinery.viatra.runtime.tabular;
11
12import org.apache.log4j.Logger;
13import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
14import tools.refinery.viatra.runtime.api.scope.*;
15import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
16import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
17
18import java.lang.reflect.InvocationTargetException;
19import java.util.List;
20import java.util.concurrent.Callable;
21
22/**
23 * @author Gabor Bergmann
24 *
25 * @since 2.1
26 */
27class 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.tabular;
10
11import tools.refinery.viatra.runtime.api.ViatraQueryEngine;
12import tools.refinery.viatra.runtime.api.scope.IEngineContext;
13import tools.refinery.viatra.runtime.api.scope.IIndexingErrorListener;
14import tools.refinery.viatra.runtime.api.scope.QueryScope;
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.scopes.IStorageBackend;
17import tools.refinery.viatra.runtime.matchers.scopes.TabularRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.scopes.tables.IIndexTable;
19import tools.refinery.viatra.runtime.matchers.scopes.tables.ITableWriterBinary;
20import 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 */
46public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.util;
10
11import org.apache.log4j.Logger;
12import tools.refinery.viatra.runtime.matchers.util.Preconditions;
13
14/**
15 * Centralized logger of the VIATRA Query runtime.
16 * @author Bergmann Gabor
17 *
18 */
19public 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}