aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-19 02:31:57 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-19 02:31:57 +0200
commit9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b (patch)
treefad77963c1dc9028b0a767ac3ad69a174c8eb8c6 /subprojects/viatra-runtime
parentfeat: predicate semantics (diff)
downloadrefinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.gz
refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.tar.zst
refinery-9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b.zip
chore: import VIATRA source
Make our modifications more maintainable by editing the source code directly instead of using reflection.
Diffstat (limited to 'subprojects/viatra-runtime')
-rw-r--r--subprojects/viatra-runtime/about.html26
-rw-r--r--subprojects/viatra-runtime/build.gradle.kts17
-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
95 files changed, 10085 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime/about.html b/subprojects/viatra-runtime/about.html
new file mode 100644
index 00000000..d1d5593a
--- /dev/null
+++ b/subprojects/viatra-runtime/about.html
@@ -0,0 +1,26 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
2<html>
3<!--
4 Copyright (c) 2017, Eclipse.org Foundation, Inc.
5
6 SPDX-License-Identifier: LicenseRef-EPL-Steward
7-->
8<head>
9<title>About</title>
10<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
11</head>
12<body lang="EN-US">
13<h2>About This Content</h2>
14
15<p>March 18, 2019</p>
16<h3>License</h3>
17
18<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
19Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>.
20For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
21
22<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
23apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
24indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
25</body>
26</html>
diff --git a/subprojects/viatra-runtime/build.gradle.kts b/subprojects/viatra-runtime/build.gradle.kts
new file mode 100644
index 00000000..5d6e3de6
--- /dev/null
+++ b/subprojects/viatra-runtime/build.gradle.kts
@@ -0,0 +1,17 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7plugins {
8 id("tools.refinery.gradle.java-library")
9}
10
11dependencies {
12 api(project(":refinery-viatra-runtime-base"))
13 api(libs.ecore)
14 implementation(libs.eclipse)
15 implementation(libs.emf)
16 implementation(libs.slf4j.log4j)
17}
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java
new file mode 100644
index 00000000..d5e0d51f
--- /dev/null
+++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/IExtensions.java
@@ -0,0 +1,24 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
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}